撰寫資料流
備份存放區是一種儲存媒體,例如磁碟或記憶體。 每個種類的備份存放區都會實作自己的資料流,作為 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