共用方式為


撰寫資料流

備份存放區是一種儲存媒體,例如磁碟或記憶體。 每個種類的備份存放區都會實作自己的資料流,作為 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 物件:
    • 第一個 FileStream (inputBaseStream 變數) 物件會讀取 data.txt 檔案的內容。 這是輸入資料流。
    • 第二個 FileStream (outputBaseStream 變數) 物件會將傳入的資料寫入 shifted.txt 檔案。 這是輸出資料流。
  • CipherStream (encryptStream 變數) 物件會包裝 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 物件:
    • 第一個 FileStream (inputBaseStream 變數) 物件會讀取 shifted.txt 檔案的內容。 這是輸入資料流。
    • 第二個 FileStream (outputBaseStream 變數) 物件會將傳入的資料寫入 unshifted.txt 檔案。 這是輸出資料流。
  • CipherStream (unencryptStream 變數) 物件會包裝 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

另請參閱