CodeGym /행동 /C# SELF /파일 포맷: 텍스트와 바이너리

파일 포맷: 텍스트와 바이너리

C# SELF
레벨 35 , 레슨 1
사용 가능

1. 텍스트 파일

기억해봐: 컴퓨터는 모든 걸, 진짜 모든 걸 1과 0으로 저장해 (아니면, 무서운 단어 안 무서우면 — 바이트 시퀀스로). 근데 똑같은 파일을 두 개의 프로그램에 먹이면, 똑같이 반응할 거란 보장은 없어. 예를 들어, 한 프로그램은 그냥 텍스트로 보는데, 다른 건 "깨진 바이트" 덩어리로 볼 수도 있지. 이게 바로 파일 포맷 때문이야 — 바이트를 어떻게 해석하냐가 중요한 거지. 포맷은 내부 데이터 구조, 읽는 규칙, 인코딩을 설명해. 포맷을 모르면 프로그램이 파일에 뭐가 들어있는지 이해 못할 수도 있어.

텍스트 파일은 데이터가 일반 텍스트(문자열)로 저장된 파일이야. 인코딩은 보통 UTF-8, 아니면 좀 하드코어하게 ASCII. 이런 파일은 아무 텍스트 에디터(예: Notepad, VS Code)로 열어서 바로 읽을 수 있어 — 그냥 문자 줄들이지, 이상한 제어 바이트나 바이너리 구조가 아니야.

대표적인 예시

  • *.txt — 일반 텍스트 파일, 예를 들어 "to-do.txt"나 "best-books.txt" 같은 거.
  • *.csv — 데이터가 쉼표나 세미콜론으로 구분된 파일(표 데이터에 딱 좋아).
  • *.json — 구조화된 데이터 교환용 파일(예: 책 리스트).
  • *.xml,*.html — 구조화된 데이터 저장/전송용.

CSV 파일 예시

;프랭크 허버트;1965
1984;조지 오웰;1949
마스터와 마르가리타;미하일 불가코프;1966
books.txt (CSV 포맷) 예시 내용

각 줄이 하나의 레코드(예: 책)야. 줄 안의 값들은 세미콜론으로 구분돼서, 이 파일이 CSV(Comma-Separated Values) 예시가 되는 거지. Comma가 원래 쉼표인데, 여기선 다른 구분자(;)를 쓴 거야. 사람이 보는 문자들이랑 프로그램이 읽는 바이트가 똑같아, 단 프로그램이 텍스트 포맷과 인코딩을 제대로 알아야 해.

프로그램이 텍스트 파일을 다루는 방법

파일을 "텍스트"로 열면 줄 단위로 읽을 수 있어. 예시(나중에 강의에서 쓸 거야):


// 파일에서 줄 단위로 간단히 읽기(자세한 건 곧 배울 거야)
string[] lines = File.ReadAllLines("books.txt");
foreach (string line in lines)
{
    Console.WriteLine(line);
}

텍스트 파일의 장점

  • 사람이 읽을 수 있음 (human-readable).
  • 직접 수정하기 쉬움(심지어 Notepad 같은 기본 앱에서도).
  • 디버깅이나 시스템 간 데이터 교환에 딱 좋아.
  • 윈도우, 리눅스, 맥 등 플랫폼 이동이 쉬움.

텍스트 파일의 단점

  • 엄격한 구조가 없음(txt), 포맷 실수하기 쉬움.
  • 공간을 더 많이 차지함(예: 12345는 5바이트, 바이너리는 4바이트).
  • 복잡한 데이터(이미지, 오디오 등)는 텍스트로 다루기 불편하거나 불가능.
  • 대용량 데이터에선 느림.

알아두면 좋은 점

  • 플랫폼마다 "줄 끝"을 다르게 씀: 윈도우 — \r\n, 유닉스 — \n.
  • 인코딩이 다를 수 있음(UTF-8, UTF-16, ANSI 등). 그래서 한 시스템에서 쓴 파일이 다른 데선 "중국어"처럼 보일 수도 있어(사실은 바이트 해석이 잘못된 거).

2. 바이너리 파일

바이너리 파일은 문자 대신 임의의 바이트가 저장되는 파일이야. 이 바이트들은 숫자, 구조체, 이미지, 음악, 심지어 머신 코드일 수도 있어. .exe 파일을 메모장으로 열어보면, 이상한 문자 덩어리만 보일 거야!

대표적인 예시

  • *.exe, *.dll — 실행 파일과 라이브러리.
  • *.jpg, *.png, *.gif— 이미지 파일.
  • *.mp3, *.wav— 오디오 파일.
  • *.dat — 임의의 사용자 데이터 파일.
  • 앱 전용 포맷(*.docx, *.xlsx 등) — 거의 다 바이너리야.

바이너리 파일 구조는?

프로그램이 바이트 단위로 데이터를 원하는 포맷대로 읽고 써. 예를 들어, int 타입 숫자는 4바이트(32비트)로 저장되고, 문자열은 보통 길이 + 문자 바이트(특정 인코딩)로 저장돼.

텍스트 파일에서 12345는 '1', '2', '3', '4', '5' 문자에 해당하는 바이트야:

문자 UTF-8 코드
1 49
2 50
3 51
4 52
5 53

바이너리 파일에서 int 12345 (리틀 엔디안)는 이렇게 저장돼:

위치 바이트 (hex)
0 0x39 57
1 0x30 48
2 0x00 0
3 0x00 0

(즉, 12345 = 0x00003039.)

같은 숫자를 텍스트와 바이너리로 저장해보고 파일 크기를 비교해봐. 바이너리가 더 작을 거야.

프로그램이 바이너리 파일을 다루는 방법

특수 스트림(예: BinaryReader, BinaryWriter)을 써. 텍스트 파일과 달리 구조를 정확히 알아야 데이터 읽기가 가능해.


// 숫자를 바이너리 파일에 쓰는 예시
var writer = new BinaryWriter(File.Open("books.bin", FileMode.Create));

// 예를 들어, 인코딩: 문자 개수(int), 그 다음 문자 바이트
string title = "듄";
writer.Write(title.Length); // 길이(int) 저장
writer.Write(title); // 문자열 저장(UTF-8로 저장됨)
writer.Write(1965); // 연도(int) 저장

writer.Close(); // 스트림 닫기

이건 그냥 예시야. 바이너리 파일 다루는 건 곧 더 자세히 배울 거야.

바이너리 파일의 장점

  • 컴팩트함: 공간을 덜 차지함.
  • 읽기/쓰기 속도가 빠름(특히 대용량 데이터에서).
  • 복잡한 구조(배열, 중첩 객체 등) 저장 가능.

바이너리 파일의 단점

  • 사람이 읽을 수 없음 (human-unreadable).
  • 파일 구조가 바뀌면, 옛날 데이터가 깨지거나 못 쓸 수도 있음.
  • 항상 정확한 파싱 규칙이 필요함.

3. 어떤 포맷을 쓸까?

텍스트 vs 바이너리 파일

기준 텍스트 파일 바이너리 파일
사람이 읽을 수 있음 아니오
크기 더 큼 더 작음
범용성 높음(아무 에디터나 가능) 특수 프로그램만 가능
속도 느림 빠름
포맷 엄격성 없음 필수로 지켜야 함
구조 저장 어려움 쉬움
데이터 손상 위험 낮음(수정 쉬움) 높음(조금만 깨져도 못 읽음)

선택 기준

  • 사람이 읽고, 쉽게 수정/디버깅해야 해? — 텍스트 파일 써(예: .txt, .csv, .json).
  • 데이터가 복잡하고, 크기/속도가 중요해? — 바이너리 포맷.
  • 플랫폼 간 데이터 교환이 필요해? — 텍스트가 더 나음, 단 인코딩은 표준(UTF-8)이어야 해.
  • 앱이 데이터 많이 읽고 쓰고, 포맷이 엄격해야 해? — 바이너리 선택.

우리 콘솔 앱 "책 리스트"에서는 처음엔 텍스트 포맷(.csv.txt)이 딱이야. 나중에 구조가 복잡해지거나 속도 올리고 싶으면, 바이너리 파일이나 JSON/XML로 넘어가도 돼.

4. 유용한 팁

실생활에서 텍스트/바이너리 파일이 쓰이는 곳?

텍스트 파일:

  • 프로그램 설정(.ini.json 많이 씀).
  • 스크립트, 로그, 메모.
  • 임포트/익스포트 파일(앱 간 데이터 교환용).

바이너리 파일:

  • 프로그램, 게임, 라이브러리(아무나 못 봄).
  • 대형 DB, 압축 파일(예: .mdb, .zip).
  • 미디어: 음악, 사진, 동영상.

파일 전체를 한 번에 읽기(텍스트):


string allText = File.ReadAllText("books.txt");
// 이제 allText는 파일 전체 내용이 들어있는 긴 문자열이야

줄 단위로(리스트에 최적):


string[] lines = File.ReadAllLines("books.txt");
foreach (string line in lines)
{
    // 줄을 부분으로 나눌 수 있음
    string[] parts = line.Split(';');
    // parts[0] — 제목, parts[1] — 저자, parts[2] — 연도
}

바이너리 읽기 — 구조에 맞게


var reader = new BinaryReader(File.Open("books.bin", FileMode.Open));

int count = reader.ReadInt32(); // 책 개수
for (int i = 0; i < count; i++)
{
    string title = reader.ReadString();
    string author = reader.ReadString();
    int year = reader.ReadInt32();
    // 이제 레코드를 복원할 수 있어!
}

reader.Close();

5. 실전 예시: 책 파일 만들기

텍스트 파일에 데이터 저장

각 책을 이런 포맷의 줄로 저장할 거야:
제목;저자;최초 출판 연도


// 저장할 책 리스트
var books = new List<(string Title, string Author, int Year)>
{
    ("듄", "프랭크 허버트", 1965),
    ("1984", "조지 오웰", 1949),
    ("마스터와 마르가리타", "미하일 불가코프", 1966)
};

// 텍스트 파일로 저장
var writer = new StreamWriter("books.txt");
foreach (var book in books)
    writer.WriteLine($"{book.Title};{book.Author};{book.Year}");

writer.Close();

books.txt를 메모장으로 열면 모든 데이터를 볼 수 있어.

바이너리 파일에 데이터 저장

이번엔 똑같은 걸 더 컴팩트하게(사람이 읽을 순 없지만) 저장해보자:


var writer = new BinaryWriter(File.Open("books.bin", FileMode.Create));

writer.Write(books.Count); // 책 개수 저장
foreach (var book in books)
{
    writer.Write(book.Title); // 문자열 - 길이+바이트로 저장됨
    writer.Write(book.Author); // 문자열
    writer.Write(book.Year); // 연도(int)
}

writer.Close();

books.bin을 텍스트 에디터로 열면, 이상한 문자만 보일 거야. 이게 정상이야. 바이너리 파일은 사람이 읽으라고 만든 게 아니라, 파일을 어떻게 썼는지 정확히 아는 프로그램만 제대로 읽을 수 있어.

6. 포맷 다룰 때 흔한 실수

바이너리 파일을 텍스트로 읽으려고 하면, 문자 "죽"이 나오거나 아예 에러가 날 수 있어. 반대로 텍스트 파일을 바이너리로 열고 엄격한 구조를 기대하면 문제 생겨.

가장 흔한 실수: 데이터 구조와 읽기/쓰기 순서가 안 맞는 것. 예를 들어, 먼저 연도 쓰고, 그 다음 제목 썼는데, 읽을 땐 반대로 하면 엉뚱한 정보가 나와. 항상 같은 순서로 작업해야 해!

프로그래머들은 텍스트 파일을 "몰래" 열어서 저장/로드 로직을 체크하곤 해 — 그래서 텍스트 포맷이 처음엔 편한 거야. 바이너리는 고급 시나리오나, 저장 방식을 사용자에게 숨기고 싶을 때 좋아.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION