Tworzenie strumieni
Magazyn zapasowy jest nośnikiem magazynu, takim jak dysk lub pamięć. Każdy typ magazynu zapasowego implementuje własny strumień jako implementację Stream klasy.
Każdy typ strumienia odczytuje i zapisuje bajty z i do danego magazynu kopii zapasowych. Strumienie łączące się z magazynami zapasowymi są nazywane strumieniami podstawowymi. Strumienie podstawowe mają konstruktory z parametrami niezbędnymi do połączenia strumienia z magazynem zapasowym. Na przykład FileStream ma konstruktory, które określają parametr trybu dostępu, który określa, czy plik jest odczytywany, zapisywany do, czy oba.
Projekt System.IO klas zapewnia uproszczoną kompozycję strumienia. Możesz dołączyć strumienie podstawowe do jednego lub większej liczby strumieni przekazywania, które zapewniają odpowiednią funkcjonalność. Możesz dołączyć czytelnika lub moduł zapisywania na końcu łańcucha, aby można było łatwo odczytywać lub zapisywać preferowane typy.
Wymagania wstępne
W tych przykładach użyto pliku w postaci zwykłego tekstu o nazwie data.txt. Ten plik powinien zawierać jakiś tekst.
Przykład: Szyfrowanie i odszyfrowywanie danych strumienia
Poniższy przykład odczytuje dane z pliku, szyfruje je, a następnie zapisuje zaszyfrowane dane w innym pliku. Kompozycja strumienia służy do przekształcania danych przy użyciu podstawowego szyfrowania przesunięcia. Każdy bajt przechodzący przez strumień ma wartość zmienioną o 80.
Ostrzeżenie
Szyfrowanie używane w tym przykładzie jest podstawowe i niezabezpieczone. Nie jest przeznaczona do faktycznego szyfrowania danych do użycia, ale jest dostarczana w celu zademonstrowania zmiany danych za pomocą kompozycji strumienia.
Odczytywanie danych źródłowych na potrzeby szyfrowania
Poniższy kod odczytuje tekst z jednego pliku, przekształca go, a następnie zapisuje go w innym pliku.
Napiwek
Przed przejrzeniem tego kodu należy wiedzieć, że CipherStream
jest to typ zdefiniowany przez użytkownika. Kod tej klasy jest udostępniany w sekcji Klasa CipherStream.
void WriteShiftedFile()
{
// Create the base streams for the input and output files
using FileStream inputBaseStream = File.OpenRead("data.txt");
using CipherStream encryptStream = CipherStream.CreateForRead(inputBaseStream);
using FileStream outputBaseStream = File.Open("shifted.txt", FileMode.Create, FileAccess.Write);
int intValue;
// Read byte from inputBaseStream through the encryptStream (normal bytes into shifted bytes)
while ((intValue = encryptStream.ReadByte()) != -1)
{
outputBaseStream.WriteByte((byte)intValue);
}
// Process is:
// (inputBaseStream -> encryptStream) -> outputBaseStream
}
Sub WriteShiftedFile()
'Create the base streams for the input and output files
Using inputBaseStream As FileStream = File.OpenRead("data.txt")
Using encryptStream As CipherStream = CipherStream.CreateForRead(inputBaseStream)
Using outputBaseStream As FileStream = File.Open("shifted.txt", FileMode.Create, FileAccess.Write)
'Read byte from inputBaseStream through the encryptStream (normal bytes into shifted bytes)
Dim intValue As Integer = encryptStream.ReadByte()
While intValue <> -1
outputBaseStream.WriteByte(Convert.ToByte(intValue))
intValue = encryptStream.ReadByte()
End While
End Using
End Using
End Using
'Process is:
' (inputBaseStream -> encryptStream) -> outputBaseStream
End Sub
Rozważ następujące aspekty poprzedniego kodu:
- Istnieją dwa FileStream obiekty:
- Pierwszy
FileStream
obiekt (inputBaseStream
zmienna) odczytuje zawartość pliku data.txt . Jest to strumień danych wejściowych. - Drugi
FileStream
obiekt (outputBaseStream
zmienna) zapisuje dane przychodzące do pliku shifted.txt . Jest to strumień danych wyjściowych.
- Pierwszy
- Obiekt ( zmiennej
CipherStream
) opakowujeinputBaseStream
obiekt , tworzącinputBaseStream
strumień podstawowy dla elementuencryptStream
.encryptStream
Strumień wejściowy można odczytać bezpośrednio, zapisując dane do strumienia wyjściowego, ale nie przekształciłoby to danych. Zamiast tego otoka strumienia wejściowego encryptStream
służy do odczytywania danych. Gdy dane są odczytywane z encryptStream
elementu , pobierają z strumienia podstawowego inputBaseStream
, przekształcają je i zwracają. Zwrócone dane są zapisywane w pliku outputBaseStream
, który zapisuje dane w pliku shifted.txt .
Odczytywanie przekształconych danych na potrzeby odszyfrowywania
Ten kod odwraca szyfrowanie wykonywane przez poprzedni kod:
void ReadShiftedFile()
{
int intValue;
// Create the base streams for the input and output files
using FileStream inputBaseStream = File.OpenRead("shifted.txt");
using FileStream outputBaseStream = File.Open("unshifted.txt", FileMode.Create, FileAccess.Write);
using CipherStream unencryptStream = CipherStream.CreateForWrite(outputBaseStream);
// Read byte from inputBaseStream through the encryptStream (shifted bytes into normal bytes)
while ((intValue = inputBaseStream.ReadByte()) != -1)
{
unencryptStream.WriteByte((byte)intValue);
}
// Process is:
// inputBaseStream -> (encryptStream -> outputBaseStream)
}
Sub ReadShiftedFile()
'Create the base streams for the input and output files
Using inputBaseStream As FileStream = File.OpenRead("shifted.txt")
Using outputBaseStream As FileStream = File.Open("unshifted.txt", FileMode.Create, FileAccess.Write)
Using unencryptStream As CipherStream = CipherStream.CreateForWrite(outputBaseStream)
'Read byte from inputBaseStream through the encryptStream (shifted bytes into normal bytes)
Dim intValue As Integer = inputBaseStream.ReadByte()
While intValue <> -1
unencryptStream.WriteByte(Convert.ToByte(intValue))
intValue = inputBaseStream.ReadByte()
End While
End Using
End Using
End Using
End Sub
Rozważ następujące aspekty poprzedniego kodu:
- Istnieją dwa FileStream obiekty:
- Pierwszy
FileStream
obiekt (inputBaseStream
zmienna) odczytuje zawartość pliku shifted.txt . Jest to strumień danych wejściowych. - Drugi
FileStream
obiekt (outputBaseStream
zmienna) zapisuje dane przychodzące do pliku unshifted.txt . Jest to strumień danych wyjściowych.
- Pierwszy
- Obiekt ( zmiennej
CipherStream
) opakowujeoutputBaseStream
obiekt , tworzącoutputBaseStream
strumień podstawowy dla elementuunencryptStream
.unencryptStream
W tym miejscu kod różni się nieco od poprzedniego przykładu. Zamiast opakowować strumień wejściowy, unencryptStream
opakowuje strumień wyjściowy. Ponieważ dane są odczytywane ze inputBaseStream
strumienia wejściowego, są wysyłane do otoki strumienia wyjściowego unencryptStream
. Gdy unencryptStream
odbiera dane, przekształca je, a następnie zapisuje dane w strumieniu outputBaseStream
podstawowym. Strumień outputBaseStream
wyjściowy zapisuje dane w pliku unshifted.txt .
Weryfikowanie przekształconych danych
Dwa poprzednie przykłady wykonały dwie operacje na danych. Najpierw zawartość pliku data.txt została zaszyfrowana i zapisana w pliku shifted.txt . Po drugie, zaszyfrowana zawartość pliku shifted.txt została odszyfrowana i zapisana w pliku unshifted.txt . W związku z tym plik data.txt i plik unshifted.txt powinny być dokładnie takie same. Poniższy kod porównuje te pliki pod kątem równości:
bool IsShiftedFileValid()
{
// Read the shifted file
string originalText = File.ReadAllText("data.txt");
// Read the shifted file
string shiftedText = File.ReadAllText("unshifted.txt");
// Check if the decrypted file is valid
return shiftedText == originalText;
}
Function IsShiftedFileValid() As Boolean
'Read the shifted file
Dim originalText As String = File.ReadAllText("data.txt")
'Read the shifted file
Dim shiftedText As String = File.ReadAllText("unshifted.txt")
'Check if the decrypted file is valid
Return shiftedText = originalText
End Function
Poniższy kod uruchamia cały proces odszyfrowywania szyfrowania:
// Read the contents of data.txt, encrypt it, and write it to shifted.txt
WriteShiftedFile();
// Read the contents of shifted.txt, decrypt it, and write it to unshifted.txt
ReadShiftedFile();
// Check if the decrypted file is valid
Console.WriteLine(IsShiftedFileValid()
? "Decrypted file is valid" // True
: "Decrypted file is invalid" // False
);
// Output:
// Decrypted file is valid
Sub Main(args As String())
'Read the contents of data.txt, encrypt it, And write it to shifted.txt
WriteShiftedFile()
'Read the contents of shifted.txt, decrypt it, And write it to unshifted.txt
ReadShiftedFile()
'Check if the decrypted file Is valid
Console.WriteLine(IIf(IsShiftedFileValid(),
"Decrypted file is valid", ' True
"Decrypted file is invalid" ' False
))
End Sub
CipherStream, klasa
Poniższy fragment kodu zawiera klasę CipherStream
, która używa podstawowego szyfrowania przesunięcia do szyfrowania i odszyfrowywania bajtów. Ta klasa dziedziczy z Stream i obsługuje odczytywanie lub zapisywanie danych.
Ostrzeżenie
Szyfrowanie używane w tym przykładzie jest podstawowe i niezabezpieczone. Nie jest przeznaczona do faktycznego szyfrowania danych do użycia, ale jest dostarczana w celu zademonstrowania zmiany danych za pomocą kompozycji strumienia.
using System.IO;
public class CipherStream : Stream
{
// WARNING: This is a simple encoding algorithm and should not be used in production code
const byte ENCODING_OFFSET = 80;
private bool _readable;
private bool _writable;
private Stream _wrappedBaseStream;
public override bool CanRead => _readable;
public override bool CanSeek => false;
public override bool CanWrite => _writable;
public override long Length => _wrappedBaseStream.Length;
public override long Position
{
get => _wrappedBaseStream.Position;
set => _wrappedBaseStream.Position = value;
}
public static CipherStream CreateForRead(Stream baseStream)
{
return new CipherStream(baseStream)
{
_readable = true,
_writable = false
};
}
public static CipherStream CreateForWrite(Stream baseStream)
{
return new CipherStream(baseStream)
{
_readable = false,
_writable = true
};
}
private CipherStream(Stream baseStream) =>
_wrappedBaseStream = baseStream;
public override int Read(byte[] buffer, int offset, int count)
{
if (!_readable) throw new NotSupportedException();
if (count == 0) return 0;
int returnCounter = 0;
for (int i = 0; i < count; i++)
{
int value = _wrappedBaseStream.ReadByte();
if (value == -1)
return returnCounter;
value += ENCODING_OFFSET;
if (value > byte.MaxValue)
value -= byte.MaxValue;
buffer[i + offset] = Convert.ToByte(value);
returnCounter++;
}
return returnCounter;
}
public override void Write(byte[] buffer, int offset, int count)
{
if (!_writable) throw new NotSupportedException();
byte[] newBuffer = new byte[count];
buffer.CopyTo(newBuffer, offset);
for (int i = 0; i < count; i++)
{
int value = newBuffer[i];
value -= ENCODING_OFFSET;
if (value < 0)
value = byte.MaxValue - value;
newBuffer[i] = Convert.ToByte(value);
}
_wrappedBaseStream.Write(newBuffer, 0, count);
}
public override void Flush() => _wrappedBaseStream.Flush();
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
}
Imports System.IO
Public Class CipherStream
Inherits Stream
Const ENCODING_OFFSET As Byte = 80
Private _readable As Boolean = False
Private _writable As Boolean = False
Private _wrappedBaseStream As Stream
Public Overrides ReadOnly Property CanRead As Boolean
Get
Return _readable
End Get
End Property
Public Overrides ReadOnly Property CanSeek As Boolean
Get
Return False
End Get
End Property
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return _writable
End Get
End Property
Public Overrides ReadOnly Property Length As Long
Get
Return _wrappedBaseStream.Length
End Get
End Property
Public Overrides Property Position As Long
Get
Return _wrappedBaseStream.Position
End Get
Set(value As Long)
_wrappedBaseStream.Position = value
End Set
End Property
Public Shared Function CreateForRead(baseStream As Stream) As CipherStream
Return New CipherStream(baseStream) With
{
._readable = True,
._writable = False
}
End Function
Public Shared Function CreateForWrite(baseStream As Stream) As CipherStream
Return New CipherStream(baseStream) With
{
._readable = False,
._writable = True
}
End Function
Private Sub New(baseStream As Stream)
_wrappedBaseStream = baseStream
End Sub
Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
If Not _readable Then Throw New NotSupportedException()
If count = 0 Then Return 0
Dim returnCounter As Integer = 0
For i = 0 To count - 1
Dim value As Integer = _wrappedBaseStream.ReadByte()
If (value = -1) Then Return returnCounter
value += ENCODING_OFFSET
If value > Byte.MaxValue Then
value -= Byte.MaxValue
End If
buffer(i + offset) = Convert.ToByte(value)
returnCounter += 1
Next
Return returnCounter
End Function
Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
If Not _writable Then Throw New NotSupportedException()
Dim newBuffer(count) As Byte
buffer.CopyTo(newBuffer, offset)
For i = 0 To count - 1
Dim value As Integer = newBuffer(i)
value -= ENCODING_OFFSET
If value < 0 Then
value = Byte.MaxValue - value
End If
newBuffer(i) = Convert.ToByte(value)
Next
_wrappedBaseStream.Write(newBuffer, 0, count)
End Sub
Public Overrides Sub Flush()
_wrappedBaseStream.Flush()
End Sub
Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long
Throw New NotSupportedException()
End Function
Public Overrides Sub SetLength(value As Long)
Throw New NotSupportedException()
End Sub
End Class