撰写流

后备存储是磁盘或内存等存储介质。 各个类型的后备存储都实现自己的流,作为 Stream 类的实现。

每个流类型对给定的后备存储执行字节读取和写入操作。 连接到后备存储的流称为“基流”。 基流的构造函数包含将流连接到后备存储所需的参数。 例如,FileStream 具有指定访问模式参数的构造函数,该参数确定对文件是读取、写入还是读写。

System.IO 类旨在简化流撰写。 可以将基流附加到一个或多个提供所需功能的直通流。 可以将读取器或编写器附加到链的末尾,这样便能轻松读取或编写首选类型。

先决条件

这些示例使用名为 data.txt 的纯文本文件。 此文件应包含一些文本。

示例:加密和解密流数据

以下示例从文件读取数据,对其进行加密,然后将加密数据写入另一个文件。 流组合用于使用基本移位密码来转换数据。 通过流的每个字节的值都更改了 80。

警告

此示例中使用的加密是基本的和不安全的。 它的目的并不是实际加密数据以供使用,而是用于演示如何通过流组合来更改数据。

读取用于加密的源数据

以下代码从一个文件读取文本,对其进行转换,然后将其写入另一个文件。

提示

在查看此代码之前,应了解 CipherStream 是用户定义的类型。 此类的代码在 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

关于前面的代码,请考虑以下方面:

  • 有两个 FileStream 对象:
    • 第一个 FileStreaminputBaseStream 变量)对象会读取 data.txt 文件的内容。 这是输入数据流。
    • 第二个 FileStreamoutputBaseStream 变量)对象将传入数据写入 shifted.txt 文件。 这是输出数据流。
  • CipherStreamencryptStream 变量)对象会包装 inputBaseStream,使 inputBaseStream 成为 encryptStream 的基流。

可以直接读取输入流,将数据写入输出流,但此操作不会转换数据。 相反,encryptStream 输入流包装器用于读取数据。 从 encryptStream 中读取数据时,它会从 inputBaseStream 基流提取数据,然后转换并返回数据。 返回的数据将写入 outputBaseStream,这会将数据写入 shifted.txt 文件。

读取转换后的数据进行解密

此代码将撤消前面的代码执行的加密:

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

关于前面的代码,请考虑以下方面:

  • 有两个 FileStream 对象:
    • 第一个 FileStreaminputBaseStream 变量)对象会读取 shifted.txt 文件的内容。 这是输入数据流。
    • 第二个 FileStreamoutputBaseStream 变量)对象将传入数据写入 unshifted.txt 文件。 这是输出数据流。
  • CipherStreamunencryptStream 变量)对象会包装 outputBaseStream,使 outputBaseStream 成为 unencryptStream 的基流。

此处,代码与前面的示例略有不同。 unencryptStream 不是包装输入流,而是包装输出流。 从 inputBaseStream 输入流中读取数据时,数据会发送到 unencryptStream 输出流包装器。 unencryptStream 接收数据时,它会转换数据,然后将数据写入 outputBaseStream 基流。 outputBaseStream 输出流会将数据写入 unshifted.txt 文件。

验证转换后的数据

前面的两个示例对数据执行了两项操作。 首先,data.txt 文件的内容已加密并保存到 shifted.txt 文件中。 其次,已解密 shifted.txt 文件的加密内容,并将其保存到 unshifted.txt 文件中。 因此,data.txt 文件和 unshifted.txt 文件应完全相同。 以下代码比较这些文件是否相等:

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

以下代码运行这一完整的加密解密过程:

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

以下代码片段提供 CipherStream 类,该类使用基本移位密码来加密和解密字节。 此类继承自 Stream 并支持读取或写入数据。

警告

此示例中使用的加密是基本的和不安全的。 它的目的并不是实际加密数据以供使用,而是用于演示如何通过流组合来更改数据。

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

另请参阅