다음을 통해 공유


스트림 작성

백업 저장소(backing store)는 디스크 또는 메모리와 같은 스토리지 매체입니다. 각 유형의 백업 저장소는 자체 스트림을 Stream 클래스의 구현으로 구현합니다.

각 스트림 유형은 지정된 백업 저장소에 유입 또는 유출되는 바이트를 읽고 씁니다. 백업 저장소에 연결되는 스트림을 기본 스트림(base stream)이라고 합니다. 기본 스트림에는 스트림을 백업 저장소에 연결하는 데 필요한 매개 변수가 포함된 생성자가 있습니다. 예를 들어 FileStream에는 파일을 읽을지, 쓸지, 아니면 둘 다 할지 결정하는 액세스 모드 매개 변수를 지정하는 생성자가 있습니다.

System.IO 클래스 디자인은 간소화된 스트림 컴퍼지션을 제공합니다. 원하는 기능을 제공하는 하나 이상의 통과 스트림에 기본 스트림을 연결할 수 있습니다. 체인 끝에 reader 또는 writer를 연결할 수 있기 때문에 기본 형식을 쉽게 읽거나 쓸 수 있습니다.

필수 조건

이러한 예제는 data.txt라는 일반 텍스트 파일을 사용합니다. 이 파일에는 일부 텍스트가 포함되어야 합니다.

예: 스트림 데이터 암호화 및 암호 해독

다음 예제에서는 파일에서 데이터를 읽고 암호화한 다음 암호화된 데이터를 다른 파일에 씁니다. 스트림 구성은 기본 변환 암호화를 사용하여 데이터를 변환하는 데 사용됩니다. 스트림을 통과하는 각 바이트의 값은 80만큼 변경됩니다.

Warning

이 예제에서 사용되는 암호화는 기본적이고 안전하지 않습니다. 실제로 데이터를 암호화하여 사용하기 위한 것이 아니라, 스트림 구성을 통해 데이터를 변경하는 방법을 보여주기 위해 제공됩니다.

암호화를 위한 원본 데이터 읽기

다음 코드는 한 파일에서 텍스트를 읽고 변환한 다음 다른 파일에 씁니다.

이 코드를 검토하기 전에 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에서 상속되며 데이터 읽기 또는 쓰기를 지원합니다.

Warning

이 예제에서 사용되는 암호화는 기본적이고 안전하지 않습니다. 실제로 데이터를 암호화하여 사용하기 위한 것이 아니라, 스트림 구성을 통해 데이터를 변경하는 방법을 보여주기 위해 제공됩니다.

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

참고 항목