Stream, FileStream, BufferedStream, MemoryStream

2023. 4. 25. 08:21Language/C#

Stream이란?

프로그램을 개발하다보면 Stream을 사용하는 일이 많습니다.

Java와 .NET 에서는 입풀력 작업을 Stream이란 개념을 통해서 추상화하여 사용합니다.

대표적으로 파일을 읽고 쓸 때 많이 사용하는데, 자세히 알아보도록 하겠습니다.

먼저 사전적인 의미를 보자면 아래와 같습니다.

  1. A stream is a small narrow river.
  2. A stream of smoke, air, or liquid is a narrow moving mass of it.
  3. A stream of vehicles or people is a long moving line of them.

(액체 등의) 좁은 흐름 우리 말로하면 작은 시냇물 같은 의미를 가지고 있습니다.

즉, 프로그램에서 Stream이란 데이터가 끊기지 않고 연속적으로 작은 단위로 주고 받는 것을 말합니다.

Stream은 일반적으로 대용량 파일에서 데이터를 읽고 쓸 때 사용됩니다. 대용량의 파일을 작은 단위로 쪼개어 데이터를 전송하는데 큰 파일을 한번에 읽거나 쓰는 것은 시스템에 큰 부담을 줍니다.

만약 100MB의 파일을 한번에 읽게 된다면 프로그램을 중단되거나 불안정하게 동작할 수 잇습니다.

Stream Reader

Stream Reader는 파일에서 데이터를 읽는 데 사용이 됩니다. 파일의 데이터를 먼저 Stream에 읽은 후 프로그램이 Stream의 데이터를 읽게 됩니다.

예를 들어 C#에서 Example.txt라는 파일을 읽을 때는 아래와 같이 코드를 작성합니다.

var path = @"D:\Example.txt";

using(StreamReader sr = File.OpenText(path))
{
    string s = "";
    while((s = sr.ReadLine()) != null)
    {
        Console.WriteLine(s);
    }
}

코드 설명

  1. StreamReader의 대상이 될 파일 경로를 설정합니다. OpenText는 읽기 전용으로 파일을 읽을 때 사용됩니다.
  2. 그런 후 파일에서 모든 데이터를 읽는데 사용 될 임시변수 s를 선언합니다.
  3. StreamReader의 ReadLine을 통해 Stream Buffer에서 한줄 한줄 읽게 되는데 ReadLine을 할 때마다 파일에서 Stream으로 기록 됩니다. 그리고 s 변수로 데이터가 쓰여집니다.

Stream Write

Stream Writer는 Stream을 사용하여 파일에 데이터를 쓰기 작업 할 때 사용됩니다. 프로그램에서 데이터를 먼저 Stream에 기록 후 Stream은 파일에 데이터를 쓰게 됩니다.


var path = @"D:\Example.txt";

using(StreamWriter sw = File.AppendText(path))
{
    sw.WriteLine("AAAA");
    sw.WriteLine("BBBB");
    sw.WriteLine("CCCC");
    sw.Close();
}

코드 설명

  1. StreamWrite의 대상이 될 파일 경로를 설정합니다. ApendText는 파일에 라인을 추가 할 때 사용합니다.
  2. 그런 후 WriteLine을 통해 Stream에 쓰기작업을 합니다. 이 때 파일에 한줄 한줄 쓰기 작업이 진행됩니다.
  3. 작업이 끝나면 StreamWriter를 Close 해줘야 합니다.

FileStream

Stream은 파일, 네트워크 연결, 메로리, 파이프, 콘솔, 디버그 및 추적 수신기 등 다른 유형의 데이터 소스에 읽기/쓰기용으로 존재하는 합니다.

반면 FileStream은 Stream을 상속 받았으며 byte array를 사용하여 파일을 읽고 쓰는 것에만 사용되는 Stream입니다.


using System.Text;

var path = @"D:\Example.txt";

using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
{
    string str = "AAAA";
    byte[] writeBuffer = Encoding.UTF8.GetBytes(str);
    fs.Write(writeBuffer, 0, writeBuffer.Length);
    fs.Close();
}

코드 설명

  1. FileStream을 선언하고 파일을 Open및 Create 모드로 생성합니다.
  2. str을 byte Array로 변경합니다.
  3. Write(buffer, offset, count)로 byte array에서 어디서 부터 얼만큼 기록을 할 것인지 지정합니다.
  4. 사용이 끝나 FileStream을 Close해 줍니다.

BufferedStream

BufferedStream는 입출력시 Buffer를 두어 입출력 횟수를 줄여 성능을 향상 시킬 때 사용됩니다.

FileStream의 경우 Byte 단위로 데이터를 읽고 쓰기 때문에 바이트당 read/write 횟수가 발생하기 때문에 오버헤드가 발생합니다.

기본적인 Buffer 사이즈는 (1024*4) 바이트 입니다.

Buffer의 크기가 128KB일 경우 버퍼링 된 작업은 버퍼링 되지 않은 작업보다 10~20% 정도 속도가 빠릅니다.

BufferedStream(Stream stream);
BufferedStream(Stream strea, int buffersize);
using System.Text;

var path = @"D:\Example.txt";

using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
{
    using(BufferedStream bs = new BufferedStream(fs))
    {
        string str = "AAAA";
        byte[] writeBuffer = Encoding.UTF8.GetBytes(str);
        bs.Write(writeBuffer, 0, writeBuffer.Length);
        bs.Close();
    }

}

코드설명

  1. FileStream을 사용하여 파일을 Open or Create 모드로 선언합니다.
  2. BufferedStream을 선언하고 FileStream을 지정하여 줍니다.
  3. AAAA를 byte[]로 변환합니다.
  4. bs.Write를 사용하여 파일에 쓰기 작업을 수행합니다.

AAAA를 byte Array로 변환하게 되면 4개의 배열이 생성됩니다.

FileStream을 사용하여 Write 작업을 하여 File을 4번 쓰기작업을 하게 됩니다.

하지만 BufferedStream을 사용하면 1번의 쓰기 작업만으로 끝나게 됩니다.

MemoryStream

MemoryStream은 디스크나 네티워크 연결 대신 메모리를 백업 저장소로 사용 하는 Stream입니다.

메모리에 바이트 데이터를 순서대로 읽고 쓰는 작업을 수행하는 클래스다. 이를 이용하면 데이터를 메모리에 직렬화/역직렬화 하는 것이 가능하다.

MemoryStream을 사용하게 되면 프로그램의 임시 버퍼 및 파일의 필요성을 줄일 수 있습니다.

멤버 설명
Capacity 이 스트림에 할당된 바이트 수를 가져오거나 설정합니다.
GetBuffer 스트림에 할당된 내용을 바이트 배열로 반환합니다.
ToArray 전체 문자열의 내용을 바이트 배열로 반환합니다.
WriteTo MemoryStream의 모든 내용을 다른 문자열 파생 타입에 출력합니다.

using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
    System.IO.StreamWriter sw = new System.IO.StreamWriter(ms, System.Text.Encoding.UTF8);

    sw.WriteLine("AAAA");
    sw.WriteLine("BBBB");
    sw.WriteLine("CCCC");
    sw.Flush();
    sw.Close();
    sw.Dispose();
}

'Language > C#' 카테고리의 다른 글

StringBuilder vs String Join  (0) 2023.06.04
상속에서 Dispose 패턴  (0) 2023.05.31
EF Core Fluent API Entity Configuration  (0) 2023.05.17
.NET Serilog 사용법  (0) 2023.04.26
.NET Core에서 Redis Job Queue 사용하기  (0) 2023.04.22