본문 바로가기
C#

[C#] BCL MemoryStream, StreamWriter/Reader, BinaryWriter/Reader

by Jcoder 2020. 11. 29.

Stream 타입은 일련의 바이트를 일관성 있게 다루는 공통 기반을 제공한다

Stream의 사전적 의미는 "흐름"인테, 프로그래밍에서 사용될 때는 일반적으로 "바이트 데이터의 흐름"을 의미하며 흐름이라는 단어에서 의미하는 Stream은 데이터를 쓰고 읽는 작업을 순서대로 하는 것이 기본 정책이다.

 

MemoryStream은 Stream 추상 클래스를 상속받은 타입니다.

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

 

static void Main(string[] args)

{

    Console.WriteLine("System.IO. MemoryStream, StreamWriter/Reader, BinaryWriter/Reader");

 

    Console.WriteLine("MemoryStream");

    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())

    {

        // 기본 쓰는 방법. btye 배열을 선언해서 문자열을 바이트로 변환.

        byte[] buf = System.Text.Encoding.UTF8.GetBytes("Hello World"); // 10바이트 문자열 Hello World

        ms.Write(buf, 0, buf.Length);

        ms.Flush(); // 중단점

[그림 1] Hello World를 담은 바이트 배열 16진수 표현
[그림 2] ms.Write 후 메모이 버퍼에 담긴 Hello World

        Console.WriteLine(System.Text.Encoding.UTF8.GetString(ms.ToArray()));

    }

 

    Console.WriteLine("StreamWriter/Reader");

    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())

    {

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

 

        sw.WriteLine("Hello World");

        sw.WriteLine("Andreson");

        sw.WriteLine("32000");

        sw.Flush(); // 중단점

[그림 3] sw 버퍼에 담긴 문자열. index 29까지 포함
[그림 4] flush() 후 ms에 담긴 문자열. index 32까지포함되어 있음.

        ms.Position = 0;

 

        System.IO.StreamReader sr = new System.IO.StreamReader(ms, System.Text.Encoding.UTF8);

        Console.WriteLine(sr.ReadToEnd());

 

        sw.Close();

        sw.Dispose();

 

        sr.Close();

        sr.Dispose();

    }

 

    Console.WriteLine("BinaryWriter/Reader");

    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())

    {

        System.IO.BinaryWriter bw = new System.IO.BinaryWriter(ms, System.Text.Encoding.UTF8);

 

        bw.Write("Hello World" + Environment.NewLine); // BinaryWriter는 WriteLine이 없습니다.

        bw.Write("Andreson" + Environment.NewLine);

        bw.Write("32000" + Environment.NewLine);

        bw.Flush(); // 중단점

[그림 5] flush() 후 ms에 담긴 문자열. index 32까지포함되어 있음.

 

        ms.Position = 0;

 

        System.IO.BinaryReader br = new System.IO.BinaryReader(ms, System.Text.Encoding.UTF8);

        Console.WriteLine(br.ReadString());

        Console.WriteLine(br.ReadString());

        Console.WriteLine(br.ReadInt32());

 

        bw.Close();

        bw.Dispose();

 

        br.Close();

        br.Dispose();

    }

}

 

여기서 중요한 것은 StreamWriter와 BinaryWriter의 flush() 후 MemoryStream에 담긴 값이 다르다.

[그림 4]와 [그림 5]의 차이점은 [그림 4]에서 _buffer[0] ~ _buffer[2] = ef bb bf 가 되어 있고 [그림 5]에서 _buffer[0] = od라 되어 있다.

 

[그림 4]에서 _buffer[0] ~ _buffer[2] = ef bb bf --> 인코딩이 UTF-8로 되어 있음을 표시하는 BOM 데이터이고 _buffer[3] ~ [13]까지는 Hello World, [14] ~ [15]는 개행문자이고 뒤에는 방금과 같은 형식이다.

 

[그림 5]에서 _buffer[0] = od --> 다음에 이어지는 의미 있는 데이터의 길이. 0x0d는 10진수로 13이므로 다음 바이트가 13바이트라는 뜻이다.

_buffer[1] ~ [13] 까지는 Hello World + 개행 (/r/n) 이고 [14] 0x0a는 10진수로 10이므로 다음 바이트가 10바이트라는 뜻이다. ([15] ~ [24])

 

이러한 차이점으로 인해 일반적으로 사람이 쉽게 읽을 수 있는 데이터를 원하는 경우 StreamWriter/Reader , 데이터의 가독성이 떨어지더라도 규격이 정해진 데이터를 입출력할땐 BinaryWriter/Reader를 사용한다.