C# 이진 파일 처리
C# 에서 이진 파일 (Binary File) 을 읽고 쓰기 위해 일반적으로 BinaryReader, BinaryWriter 클래스를 사용한다. BinaryReader, BinaryWriter 클래스는 이진 파일을 처리하는 여러 기능을 가진 클래스로서, 이진 파일을 읽을 때는 BinaryReader를, 쓸 때는 BinaryWriter를 사용한다. (주: BinaryWriter와 BinaryReader는 이진파일 스트림 뿐만 아니라 네트워크 스트림등의 다른 이진 스트림을 처리할 수 있다)
BinaryWriter를 사용한 이진데이타 쓰기
데이타를 이진파일에 쓰기 위해서는 BinaryWriter 클래스를 사용한다. BinaryWriter 객체를 생성하기 위해 먼저 File.Open() 을 사용(혹은 FileStream 클래스 사용)하여 파일스트림을 생성한 후, 이를 BinaryWriter의 생성자에 전달한다.
BinaryWriter에서 데이타를 이진파일에 쓰기 위해서는 Write() 메서드를 사용하면 된다. Write() 메서드는 바이트, bool, 정수, 숫자, 문자열 등 다양한 종류의 데이타를 쓸 수 있는데, 이 메서드 하나가 18개의 서로 다른 메서드 오버로드 (.NET 4.5 기준)를 가지고 있다.
아래 예제는 Write 메서드를 사용하여 다양한 타입의 데이타를 이진파일에 쓰는 코드이다.
BinaryWriter에서 데이타를 이진파일에 쓰기 위해서는 Write() 메서드를 사용하면 된다. Write() 메서드는 바이트, bool, 정수, 숫자, 문자열 등 다양한 종류의 데이타를 쓸 수 있는데, 이 메서드 하나가 18개의 서로 다른 메서드 오버로드 (.NET 4.5 기준)를 가지고 있다.
아래 예제는 Write 메서드를 사용하여 다양한 타입의 데이타를 이진파일에 쓰는 코드이다.
예제
using System.IO;
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
// 이진파일에 쓸 샘플데이타
bool b = true;
int i = 101;
decimal d = 1024.05M;
byte by = 0xA0;
byte[] bytes = { 0xFF, 0x32, 0x11 };
string s = "Hello";
// 이진파일 생성
FileStream fs = File.Open(@"C:\Temp\data.bin", FileMode.Create);
// BinaryWriter는 파일스트림을 사용해서 객체를 생성한다
using (BinaryWriter wr = new BinaryWriter(fs))
{
// 각각의 샘플데이타를 이진파일에 쓴다
wr.Write(b); // bool
wr.Write(i); // 정수
wr.Write(d); // decimal
wr.Write(by); // byte
wr.Write(bytes); // bytes
wr.Write(s); // 문자열 (UTF8)
}
}
}
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
// 이진파일에 쓸 샘플데이타
bool b = true;
int i = 101;
decimal d = 1024.05M;
byte by = 0xA0;
byte[] bytes = { 0xFF, 0x32, 0x11 };
string s = "Hello";
// 이진파일 생성
FileStream fs = File.Open(@"C:\Temp\data.bin", FileMode.Create);
// BinaryWriter는 파일스트림을 사용해서 객체를 생성한다
using (BinaryWriter wr = new BinaryWriter(fs))
{
// 각각의 샘플데이타를 이진파일에 쓴다
wr.Write(b); // bool
wr.Write(i); // 정수
wr.Write(d); // decimal
wr.Write(by); // byte
wr.Write(bytes); // bytes
wr.Write(s); // 문자열 (UTF8)
}
}
}
BinaryReader를 사용한 이진데이타 읽기
이진파일에 데이타를 위해서는 BinaryReader 클래스를 사용한다. BinaryWriter와 마찬가지로 BinaryReader 클래스는 이진파일스트림을 통해 동작하므로, 이진파일을 오픈해서 파일스트림을 얻고 이를 통해 BinaryReader 객체를 생성한다.
BinaryReader 클래스는 이진데이타를 읽기 위해서 각 데이타 타입별로 다양한 메서드들을 가지고 있다. 예를 들어, 부울린 타입은 ReadBoolean()을, 32비트 정수는 ReadInt32(), 16비트 정수는 ReadInt16()을, 한 바이트는 ReadByte(), 여러 개의 바이트들은 ReadBytes(), 문자열을 ReadString() 등을 사용한다. BinaryWriter 클래스는 여러 데이타 타입에 대해 메서드 오버로딩을 사용해 하나의 Write() 메서드를 사용하지만, BinaryReader 클래스는 각 데이타 타입별로 다른 메서드를 사용한다. BinaryReader 클래스의 Read 메서드들은 해당 타입의 데이타를 읽고 그 데이타 크기만큼 현재 파일위치에서 이동한다.
BinaryReader 클래스는 이진데이타를 읽기 위해서 각 데이타 타입별로 다양한 메서드들을 가지고 있다. 예를 들어, 부울린 타입은 ReadBoolean()을, 32비트 정수는 ReadInt32(), 16비트 정수는 ReadInt16()을, 한 바이트는 ReadByte(), 여러 개의 바이트들은 ReadBytes(), 문자열을 ReadString() 등을 사용한다. BinaryWriter 클래스는 여러 데이타 타입에 대해 메서드 오버로딩을 사용해 하나의 Write() 메서드를 사용하지만, BinaryReader 클래스는 각 데이타 타입별로 다른 메서드를 사용한다. BinaryReader 클래스의 Read 메서드들은 해당 타입의 데이타를 읽고 그 데이타 크기만큼 현재 파일위치에서 이동한다.
예제
using System;
using System.IO;
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
// BinaryReader는 스트림을 사용해서 객체를 생성한다
using (BinaryReader rdr = new BinaryReader(File.Open(@"C:\Temp\data.bin", FileMode.Open)))
{
// 각 데이타 타입별로 다양한 Read 메서드를 사용한다
bool b = rdr.ReadBoolean();
int i = rdr.ReadInt32();
decimal d = rdr.ReadDecimal();
byte by = rdr.ReadByte();
byte[] bytes = rdr.ReadBytes(3); // 3바이트 읽기
string s = rdr.ReadString();
Console.WriteLine("{0},{1},{2}", b, i, s);
}
}
}
}
using System.IO;
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
// BinaryReader는 스트림을 사용해서 객체를 생성한다
using (BinaryReader rdr = new BinaryReader(File.Open(@"C:\Temp\data.bin", FileMode.Open)))
{
// 각 데이타 타입별로 다양한 Read 메서드를 사용한다
bool b = rdr.ReadBoolean();
int i = rdr.ReadInt32();
decimal d = rdr.ReadDecimal();
byte by = rdr.ReadByte();
byte[] bytes = rdr.ReadBytes(3); // 3바이트 읽기
string s = rdr.ReadString();
Console.WriteLine("{0},{1},{2}", b, i, s);
}
}
}
}
이진파일에서 문자열 인코딩
이진파일에 문자열 혹은 문자를 읽거나 쓸 때, BinaryReader / BinaryWriter 클래스는 디폴트로 UTF-8 인코딩을 사용한다. 만약 이진파일이 디폴트 인코딩과 인코딩을 사용한다면, BinaryReader / BinaryWriter 생성자에서 해당 인코딩을 지정해 주어야 한다. 아래 예제는 이진파일에 문자를 쓸 때는 UTF7 인코딩을 사용하고 이를 읽을 때는 디폴트 인코딩 (UTF8)을 사용해 본 코드이다. 이 예제의 결과는 깨진 문자가 읽혀지게 된다.
예제
using System;
using System.IO;
using System.Text;
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
string datafile = "data.bin";
FileStream fw = File.Open(datafile, FileMode.Create);
using (BinaryWriter wr = new BinaryWriter(fw, Encoding.UTF7))
{
// 문자 데이타에 UTF7 인코딩이 사용됨
wr.Write("안녕하세요?");
wr.Write('한');
}
var fr = File.Open(datafile, FileMode.Open);
// using (BinaryReader rdr = new BinaryReader(fr, Encoding.UTF7)) // 맞는 코드
using (BinaryReader rdr = new BinaryReader(fr)) // 틀린코드 (디폴트 UTF8)
{
// 문자 데이타 읽기. 지정된 인코딩 사용함.
string s = rdr.ReadString();
char c = rdr.ReadChar();
Console.WriteLine("{0},{1}", s, c);
}
}
}
}
using System.IO;
using System.Text;
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
string datafile = "data.bin";
FileStream fw = File.Open(datafile, FileMode.Create);
using (BinaryWriter wr = new BinaryWriter(fw, Encoding.UTF7))
{
// 문자 데이타에 UTF7 인코딩이 사용됨
wr.Write("안녕하세요?");
wr.Write('한');
}
var fr = File.Open(datafile, FileMode.Open);
// using (BinaryReader rdr = new BinaryReader(fr, Encoding.UTF7)) // 맞는 코드
using (BinaryReader rdr = new BinaryReader(fr)) // 틀린코드 (디폴트 UTF8)
{
// 문자 데이타 읽기. 지정된 인코딩 사용함.
string s = rdr.ReadString();
char c = rdr.ReadChar();
Console.WriteLine("{0},{1}", s, c);
}
}
}
}
File 클래스를 통해 전체 바이트 읽고 쓰기
File 클래스는 이진파일 전체를 읽어 바이트배열로 리턴하는 File.ReadAllBytes() 메서드와 바이트 배열을 이진파일에 쓰는 File.WriteAllBytes() 메서드를 제공하고 있다. 이들 메서드는 바이트 전체를 간단하게 읽고 쓸 때 편리하게 사용할 수 있다.
예제
using System.IO;
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
// 이미지 파일에서 전체 바이트 읽기
byte[] bytes = File.ReadAllBytes(@"C:\Temp\source.png");
// 이미지 파일에 모든 바이트 쓰기
File.WriteAllBytes(@"C:\Temp\target.png", bytes);
}
}
}
namespace FileApp
{
class Program
{
static void Main(string[] args)
{
// 이미지 파일에서 전체 바이트 읽기
byte[] bytes = File.ReadAllBytes(@"C:\Temp\source.png");
// 이미지 파일에 모든 바이트 쓰기
File.WriteAllBytes(@"C:\Temp\target.png", bytes);
}
}
}
FileStream 클래스를 통한 파일 읽고 쓰기
FileStream 클래스는 기본적인 Low 레벨의 파일 I/O 를 담당하는 클래스이다. 이 클래스는 파일스트림을 바이트 레벨에서 읽고 쓰는 기능을 가지고 있으며, 비동기 처리 기능도 가지고 있다. (위의 File 클래스는 파일 I/O 처리에서 내부적으로 FileStream을 사용한다). FileStream의 Read(), Write() 메서드는 파일의 바이트 데이타를 부분적으로 읽고 쓰는 기능을 가지고 있으며, 사이즈가 큰 파일을 일부씩만 읽어 처리할 때 특히 유용하다.
아래 예제는 이미지 파일을 10번에 걸쳐 나누어 읽는 예이다.
아래 예제는 이미지 파일을 10번에 걸쳐 나누어 읽는 예이다.
예제
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (FileStream fs = new FileStream(@"C:\Temp\source.png", FileMode.Open))
{
int size = (int)fs.Length;
byte[] buff = new byte[size];
// 이미지를 10번에 걸쳐 나누어 읽음
for (int i = 0; i < 10; i++)
{
int n = fs.Read(buff, 0, size / 10);
//....
Console.WriteLine(n);
}
}
}
}
}
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (FileStream fs = new FileStream(@"C:\Temp\source.png", FileMode.Open))
{
int size = (int)fs.Length;
byte[] buff = new byte[size];
// 이미지를 10번에 걸쳐 나누어 읽음
for (int i = 0; i < 10; i++)
{
int n = fs.Read(buff, 0, size / 10);
//....
Console.WriteLine(n);
}
}
}
}
}