Freigeben über


Erstellen von Streams

Ein Sicherungsspeicher ist ein Speichermedium, wie etwa ein Datenträger oder Arbeitsspeicher. Jede Art von Sicherungsspeicher implementiert einen eigenen Datenstrom als Implementierung der Stream-Klasse.

Jeder Datenstromtyp liest und schreibt Bytes in einen bzw. aus einem angegebenen Sicherungsspeicher. Datenströme, die eine Verbindung mit Sicherungsspeichern herstellen, werden als Basisdatenströme bezeichnet. Basisdatenströme umfassen Konstruktoren mit den erforderlichen Parametern, um eine Verbindung zwischen dem Datenstrom und dem Sicherungsspeicher herzustellen. FileStream verfügt beispielsweise über Konstruktoren, die einen Zugriffsmodusparameter angeben, der bestimmt, ob die Datei ausgelesen, beschrieben oder beides wird.

Der Entwurf der System.IO-Klassen ermöglicht ein einfacheres Erstellen von Datenströmen. Sie können Basisdatenströme an mindestens einen Pass-Through-Datenstrom anfügen, der die von Ihnen gewünschte Funktionalität bereitstellt. Sie können einen Reader oder Writer an das Ende der Kette anfügen, damit die bevorzugten Typen mühelos gelesen oder geschrieben werden können.

Voraussetzungen

In diesen Beispielen wird eine Nur-Text-Datei namens data.txtverwendet. Diese Datei sollte Text enthalten.

Beispiel: Verschlüsseln und Entschlüsseln von Datenstromdaten

Im folgenden Beispiel werden Daten aus einer Datei gelesen, verschlüsselt und dann verschlüsselt in eine andere Datei geschrieben. Die Datenstromkomposition wird verwendet, um die Daten mithilfe einer einfachen Verschiebechiffre zu transformieren. Der Wert eines jeden Byte, das den Datenstrom durchläuft, wird um 80geändert.

Warnung

Der in diesem Beispiel verwendete Verschlüsselungsmechanismus ist einfach und unsicher. Es ist nicht dafür vorgesehen, verwendete Daten zu verschlüsseln, sondern soll lediglich veranschaulichen, wie Daten über die Datenstromkomposition geändert werden können.

Lesen der Quelldaten für die Verschlüsselung

Der folgende Code liest den Text aus einer Datei, transformiert ihn und schreibt ihn dann in eine andere Datei.

Tipp

Bevor Sie sich diesen Code genauer ansehen, sollten Sie wissen, dass es sich bei CipherStream um einen benutzerdefinierten Typ handelt. Den Code dieser Klasse finden Sie im Absatz CipherStream-Klasse.

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

Beim vorherigen Code gibt es Folgendes zu beachten:

  • Es gibt zwei FileStream-Objekte:
    • Das erste FileStream-Objekt (inputBaseStream-Variable) liest den Inhalt der Datei data.txt. Dies ist der Eingabedatenstrom.
    • Das zweite FileStream-Objekt (outputBaseStream-Variable) schreibt eingehende Daten in die Datei shifted.txt. Dies ist der Ausgabedatenstrom.
  • Das CipherStream-Objekt (encryptStream-Variable) umschließt inputBaseStream, wodurch inputBaseStream der Basisdatenstrom für encryptStream wird.

Der Eingabestream könnte direkt gelesen werden, wobei die Daten in den Ausgabestream geschrieben werden. Dadurch würden die Daten jedoch nicht transformiert werden. Stattdessen wird der Eingabestreamwrapper encryptStream verwendet, um die Daten zu lesen. Während die Daten aus encryptStream gelesen werden, werden Daten aus dem Basisdatenstrom inputBaseStream abgerufen, transformiert und zurückgegeben. Die zurückgegebenen Daten werden in outputBaseStream geschrieben, wodurch sie wiederum in die Datei shifted.txt geschrieben werden.

Lesen der transformierten Daten für die Entschlüsselung

Mit diesem Code wird die vom vorherigen Code ausgeführte Verschlüsselung rückgängig gemacht:

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

Beim vorherigen Code gibt es Folgendes zu beachten:

  • Es gibt zwei FileStream-Objekte:
    • Das erste FileStream-Objekt (inputBaseStream-Variable) liest den Inhalt der Datei shifted.txt. Dies ist der Eingabedatenstrom.
    • Das zweite FileStream-Objekt (outputBaseStream-Variable) schreibt eingehende Daten in die Datei unshifted.txt. Dies ist der Ausgabedatenstrom.
  • Das CipherStream-Objekt (unencryptStream-Variable) umschließt outputBaseStream, wodurch outputBaseStream der Basisdatenstrom für unencryptStream wird.

Dieser Code unterscheidet sich geringfügig vom vorherigen Beispiel. Anstatt den Eingabestream zu umschließen, umschließt unencryptStream den Ausgabestream. Während die Daten aus dem Eingabedatenstrom inputBaseStream gelesen werden, werden die an den unencryptStream-Ausgabestreamwrapper gesendet. Wenn unencryptStream Daten empfängt, werden diese transformiert und dann in den Basisdatenstrom outputBaseStream geschrieben. Der outputBaseStream-Ausgabestream schreibt die Daten in die Datei unshifted.txt.

Validieren der transformierten Daten

In den beiden vorherigen Beispielen wurden zwei Vorgänge für die Daten ausgeführt. Zunächst wurde der Inhalt der Datei data.txt verschlüsselt und in der Datei shifted.txt gespeichert. Anschließend wurden die verschlüsselten Inhalte der Datei shifted.txt entschlüsselt und in der Datei unshifted.txt gespeichert. Daher sollten die Datei data.txt und die Datei unshifted.txt identisch sein. Der folgende Code vergleicht diese Dateien auf Gleichheit:

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

Mit folgendem Code wird der gesamte Verschlüsselungs- und Entschlüsselungsvorgang ausgeführt:

// 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-Klasse

Der folgende Codeausschnitt stellt die CipherStream-Klasse bereit, die eine grundlegende Verschiebechiffre zum Verschlüsseln und Entschlüsseln von Bytes verwendet. Diese Klasse erbt von Stream und unterstützt entweder das Lesen oder Schreiben von Daten.

Warnung

Der in diesem Beispiel verwendete Verschlüsselungsmechanismus ist einfach und unsicher. Es ist nicht dafür vorgesehen, verwendete Daten zu verschlüsseln, sondern soll lediglich veranschaulichen, wie Daten über die Datenstromkomposition geändert werden können.

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

Siehe auch