次の方法で共有


ストリームの作成

バッキング ストアは、ディスクやメモリなどの記憶域メディアです。 バッキング ストアの各種類では、Stream クラスの実装として独自のストリームが実装されています。

各ストリームの種類は、指定されたバッキング ストアとの間でバイトの読み取りと書き込みを行います。 バッキング ストアに接続するストリームは、基本ストリームと呼ばれます。 基本ストリームにはコンストラクターがあり、ストリームをバッキング ストアに接続するために必要なパラメーターがそれに指定されます。 たとえば、FileStream にはアクセス モード パラメーターを指定するコンストラクターがあり、それによってファイルが読み取り元か、書き込み先か、またはその両方かが決まります。

System.IO クラスは、簡易なストリーム構成で設計されています。 必要な機能を提供する 1 つ以上のパススルー ストリームに基本ストリームをアタッチできます。 好みの種類の読み取りまたは書き込みを簡単に実行できるように、チェーンの末端に閲覧者またはライターをアタッチできます。

前提条件

これらの例では、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

前のコードについて、次の点を考慮してください。

  • 2 つの FileStream オブジェクトがあります。
    • 1 つ目の FileStream (inputBaseStream 変数) オブジェクトは、data.txt ファイルの内容を読み取ります。 これは、入力データ ストリームです。
    • 2 つ目の FileStream (outputBaseStream 変数) オブジェクトは、着信したデータを shifted.txt ファイルに書き込みます。 これは、出力データ ストリームです。
  • CipherStream (encryptStream 変数) オブジェクトは、inputBaseStream をラップし、inputBaseStreamencryptStream の基本ストリームにします。

入力ストリームを直接読み取り、出力ストリームにデータを書き込むことができますが、それではデータは変換されません。 代わりに、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

前のコードについて、次の点を考慮してください。

  • 2 つの FileStream オブジェクトがあります。
    • 1 つ目の FileStream (inputBaseStream 変数) オブジェクトは、shifted.txt ファイルの内容を読み取ります。 これは、入力データ ストリームです。
    • 2 つ目の FileStream (outputBaseStream 変数) オブジェクトは、着信したデータを unshifted.txt ファイルに書き込みます。 これは、出力データ ストリームです。
  • CipherStream (unencryptStream 変数) オブジェクトは、outputBaseStream をラップし、outputBaseStreamunencryptStream の基本ストリームにします。

ここのコードは、前の例と少し異なります。 入力ストリームをラップする代わりに、unencryptStream は出力ストリームをラップします。 データは、入力ストリームから inputBaseStream 読み取られると、unencryptStream 出力ストリーム ラッパーに送信されます。 unencryptStream は、データを受け取ると、それを変換してから、データを outputBaseStream 基本ストリームに書き込みます。 outputBaseStream 出力ストリームは、データを unshifted.txt ファイルに書き込みます。

変換後のデータを検証する

前の 2 つの例では、データに対して 2 つの操作を実行しました。 まず、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

関連項目