Comporre flussi

Un archivio di backup è un supporto di archiviazione, ad esempio un disco o la memoria. Ogni tipo di archivio di backup implementa il flusso come implementazione della classe Stream.

Ogni tipo di flusso legge e scrive i byte da e nel relativo archivio di backup. I flussi che si connettono agli archivi di backup sono chiamati flussi di base. I flussi di base hanno costruttori con i parametri necessari per connettere il flusso all'archivio di backup. Ad esempio, FileStream dispone di costruttori che specificano un parametro della modalità di accesso, che determina se il file viene letto, scritto o entrambi.

La progettazione delle classi System.IO consente una composizione dei flussi semplificata. I flussi di base possono essere associati a uno o più flussi pass-through che forniscono la funzionalità desiderata. Un lettore o un writer può essere collegato alla fine della catena, in modo che i tipi preferiti possano essere letti o scritti facilmente.

Prerequisiti

Questi esempi usano un file di testo normale denominato data.txt. Il file deve contenere testo.

Esempio: crittografare e decrittografare i dati del flusso

L'esempio seguente legge i dati da un file, lo crittografa e quindi scrive i dati crittografati in un altro file. La composizione del flusso viene usata per trasformare i dati usando una crittografia di base di spostamento. Ciascun byte che passa attraverso il flusso ha il valore modificato di 80.

Avviso

La crittografia usata in questo esempio è di base e non sicura. Non è progettata per crittografare effettivamente i dati per l'uso, ma viene fornita per dimostrare la modifica dei dati tramite la composizione del flusso.

Leggere i dati di origine per la crittografia

Il codice seguente legge il testo da un file, lo trasforma e lo scrive in un altro file.

Suggerimento

Prima di esaminare questo codice, è necessario sapere che CipherStream è un tipo definito dall'utente. Il codice per questa classe viene fornito nella sezione della classe 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

Considerare gli aspetti seguenti relativi al codice precedente:

  • Esistono due oggetti FileStream:
    • Il primo oggetto FileStream (variabile inputBaseStream) legge il contenuto del file data.txt. Questo è il flusso di dati di input.
    • Il secondo oggetto FileStream (variabile outputBaseStream) scrive i dati in ingresso nel file shifted.txt. Si tratta del flusso di dati di output.
  • L'oggetto CipherStream (variabile encryptStream) esegue il wrapping di inputBaseStream, rendendo inputBaseStream il flusso di base per encryptStream.

Il flusso di input può essere letto direttamente, scrivendo i dati nel flusso di output, ma non trasformando i dati. Il wrapper del flusso di input encryptStream viene invece usato per leggere i dati. Man mano che i dati vengono letti da encryptStream, esegue il pull dal flusso di base inputBaseStream, lo trasforma e lo restituisce. I dati restituiti vengono scritti in outputBaseStream, che scrive i dati nel file shifted.txt.

Leggere i dati trasformati per la decrittografia

Questo codice inverte la crittografia eseguita dal codice precedente:

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

Considerare gli aspetti seguenti relativi al codice precedente:

  • Esistono due oggetti FileStream:
    • Il primo oggetto FileStream (variabile inputBaseStream) legge il contenuto del file shifted.txt. Questo è il flusso di dati di input.
    • Il secondo oggetto FileStream (variabile outputBaseStream) scrive i dati in ingresso nel file unshifted.txt. Si tratta del flusso di dati di output.
  • L'oggetto CipherStream (variabile unencryptStream) esegue il wrapping di outputBaseStream, rendendo outputBaseStream il flusso di base di unencryptStream.

In questo caso, il codice è leggermente diverso dall'esempio precedente. Anziché eseguire il wrapping del flusso di input, unencryptStream effettua il wrapping del flusso di output. Man mano che i dati vengono letti dal flusso di input inputBaseStream, vengono inviati al wrapper del flusso di output unencryptStream. Quando unencryptStream riceve i dati, li trasforma e quindi li scrive nel flusso di base outputBaseStream. Il flusso di output outputBaseStream scrive i dati nel file unshifted.txt.

Convalidare i dati trasformati

I due esempi precedenti hanno eseguito due operazioni sui dati. Nella prima, il contenuto del file data.txt è stato crittografato e salvato nel file shifted.txt. Nella seconda, il contenuto crittografato del file shifted.txt è stato decrittografato e salvato nel file unshifted.txt. Pertanto, i file data.txt e unshifted.txt devono essere esattamente gli stessi. Il codice seguente confronta tali file per verificarne l'uguaglianza:

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

Il codice riportato di seguito esegue l'intero processo di decrittografia della crittografia:

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

Classe CipherStream

Il frammento di codice seguente fornisce la classe CipherStream, che utilizza una crittografia di base per crittografare e decrittografare i byte. Questa classe eredita da Stream e supporta la lettura o la scrittura di dati.

Avviso

La crittografia usata in questo esempio è di base e non sicura. Non è progettata per crittografare effettivamente i dati per l'uso, ma viene fornita per dimostrare la modifica dei dati tramite la composizione del flusso.

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

Vedi anche