Partager via


Composer des flux

Un magasin de stockage est un support de stockage, au même titre qu’un disque ou qu’une mémoire. Chaque magasin de stockage implémente son propre flux en tant qu’implémentation de la classe Stream.

Chaque type de flux lit et écrit des octets depuis et vers le magasin de stockage donné. Les flux qui se connectent aux magasins de stockage sont appelés des flux de base. Les flux de base comprennent des constructeurs qui ont les paramètres nécessaires pour connecter le flux au magasin de stockage. Par exemple, FileStream a des constructeurs spécifiant un paramètre de mode d’accès qui détermine si le fichier est la source de lecture, est la destination d’écriture, ou les deux.

La conception des classes System.IO fournit une composition simplifiée des flux. Vous pouvez attacher des flux de base à un ou plusieurs flux directs qui fournissent les fonctionnalités souhaitées. Vous pouvez attacher un lecteur ou un enregistreur à la fin de la chaîne pour que les types préférés puissent être lus ou écrits facilement.

Prérequis

Ces exemples utilisent un fichier texte brut nommé data.txt. Ce fichier doit contenir du texte.

Exemple : chiffrer et déchiffrer des données de flux

L’exemple suivant lit les données d’un fichier, les chiffre, puis écrit les données chiffrées dans un autre fichier. La composition de flux est utilisée pour transformer les données en utilisant un chiffrement décalé de base. Chaque octet passant via le flux a sa valeur remplacée par 80.

Avertissement

Le chiffrement utilisé dans cet exemple est basique et non sécurisé. Il n’est pas destiné à chiffrer réellement des données à utiliser. Il est fourni pour montrer la modification de données via une composition de flux.

Lecture des données source pour le chiffrement

Le code suivant lit le texte à partir d’un fichier, le transforme, puis l’écrit dans un autre fichier.

Conseil

Avant de passer en revue ce code sachez que le type CipherStream est défini par l’utilisateur. Le code pour cette classe est fourni dans la section 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

Tenez compte des aspects suivants concernant le code précédent :

  • Il existe deux objets FileStream :
    • Le premier objet FileStream (variable inputBaseStream) lit le contenu du fichier data.txt. Il s’agit du flux de données d’entrée.
    • Le deuxième objet FileStream (variable outputBaseStream) écrit les données entrantes dans le fichier shifted.txt. Il s’agit du flux de données de sortie.
  • L’objet CipherStream (variable encryptStream) enveloppe le inputBaseStream, ce qui rend inputBaseStream le flux de base pour encryptStream.

Le flux d’entrée peut être lu directement, ce qui écrit les données dans le flux de sortie, mais ne transforme pas les données. Au lieu de cela, le wrapper de flux d’entrée encryptStream est utilisé pour la lecture des données. Du fait que les données sont lues à partir de encryptStream, il effectue un tirage (pull) à partir du flux de données de base inputBaseStream, le transforme et le retourne. Les données retournée sont écrites dans outputBaseStream qui écrit les données dans le fichier shifted.txt.

Lecture des données transformée pour le déchiffrement

Ce code inverse le chiffrement effectué par le code précédent :

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

Tenez compte des aspects suivants concernant le code précédent :

  • Il existe deux objets FileStream :
    • Le premier objet FileStream (variable inputBaseStream) lit le contenu du fichier shifted.txt. Il s’agit du flux de données d’entrée.
    • Le deuxième objet FileStream (variable outputBaseStream) écrit les données entrantes dans le fichier unshifted.txt. Il s’agit du flux de données de sortie.
  • L’objet CipherStream (variable unencryptStream) enveloppe le outputBaseStream, ce qui rend outputBaseStream le flux de base pour unencryptStream.

Ici, le code diffère légèrement de l’exemple précédent. Au lieu d’effectuer un enveloppement du flux d’entrée, unencryptStream enveloppe le flux de sortie. Du fait que les données sont lues à partir du flux d’entrée inputBaseStream, elles sont envoyées au wrapper de flux de sortie unencryptStream. Quand unencryptStream reçoit les données, il les transforme, puis les écrit dans le flux de données de base outputBaseStream. Le flux de sortie outputBaseStream écrit les données dans le fichier unshifted.txt.

Validation des données transformées

Les deux exemples précédents ont effectué deux opérations sur les données. Premièrement, le contenu du fichier data.txt a été chiffré et enregistré dans le fichier shifted.txt. Et deuxièmement, le contenu chiffré du fichier shifted.txt a été déchiffré et enregistré dans le dossier unshifted.txt. Par conséquent, le fichier data.txt et le fichier unshifted.text doivent être exactement identiques. Le code suivant compare ces fichiers pour ce qui est de l’égalité :

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

Le code suivant exécute ce processus complet de chiffrement-déchiffrement :

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

L’extrait de code suivant fournit la classe CipherStream qui utilise un chiffrement décalé de base pour chiffrer et déchiffrer des octets. Cette classe hérite de Stream et prend en charge la lecture ou l’écriture de données.

Avertissement

Le chiffrement utilisé dans cet exemple est basique et non sécurisé. Il n’est pas destiné à chiffrer réellement des données à utiliser. Il est fourni pour montrer la modification de données via une composition de flux.

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

Voir aussi