텍스트 파일 쓰기
std::ofstream으로 문자열과 숫자를 기록하고 덮어쓰기·추가 모드, 버퍼 반영 시점과 쓰기 오류를 관리합니다.
프로그램이 만든 결과를 저장하거나, 설정을 기록하거나, 실행 기록을 로그로 남기려면 파일에 써야 합니다. 앞 글에서 std::ifstream으로 읽었다면, 이번에는 출력 파일 스트림(output file stream)인 std::ofstream으로 텍스트 파일에 씁니다.
이 글은 std::ofstream으로 파일을 열고 <<로 쓰는 방법, 기존 내용을 지우는 모드와 이어 쓰는 모드의 차이, 출력이 버퍼를 거쳐 파일에 기록되는 시점, 쓰기 오류를 확인하는 위치를 다룹니다. 아래 그림은 열기부터 닫은 뒤의 상태 확인까지 이어지는 쓰기의 순서입니다.
ofstream 출력은 모드 선택부터 닫기 상태 확인까지 이어진다
호출 코드/ofstream/filebuf의 open·값 삽입·문자 전송·close 최종 동기화·상태 확인 메시지 순서가 명시적이다. 정전 영속성 보장으로 확대하지 않는다.
C++ · ofstream · sequence
쓰기는 close() 뒤 상태 확인까지 완료되어야 한다
모드가 기존 내용의 처리 방식을 정하고, 출력 연산은 먼저 스트림 버퍼를 거칩니다. 중요한 출력은 명시적으로 닫은 뒤 bad()와 fail()을 확인해야 마지막 동기화에서 드러난 오류까지 잡을 수 있습니다.
- 파일 정책을 먼저 고른다
out은 기본적으로 기존 내용을 교체하고,app은 매 쓰기 전에 파일 끝으로 이동합니다. - 연결 여부를 확인한다
is_open()은 파일 연결 여부를, 스트림의 불리언 상태는 현재 오류 비트를 확인합니다. - 기본 줄바꿈은
'\n'operator<<가 값을 문자로 서식화해 버퍼에 보내며, 필요한 경계에서만flush나endl을 사용합니다. - 중요한 출력은 명시적으로 닫는다
close()가 마지막 버퍼 동기화를 수행하게 해 소멸자 뒤에는 확인할 수 없는 오류를 앞에서 드러냅니다. - 닫은 뒤 상태를 판정한다
bad()는 심각한 I/O 오류를,fail()은failbit또는badbit를 보고합니다.
app은 매 쓰기, ate는 열 때 한 번
ate로 처음 끝에 놓인 뒤에는 위치를 바꿀 수 있지만, app은 각 쓰기 직전에 끝 위치를 강제합니다.
텍스트 변환을 끌 뿐 서식 출력을 바꾸지 않는다
binary여도 operator<<는 문자 표현을 만듭니다. 원시 바이트 블록은 write()로 보냅니다.
'\n'과 동기화 요구를 분리한다
보통은 '\n'으로 줄만 바꾸고, 외부 관찰이나 프로토콜 경계에서만 std::flush 또는 std::endl을 사용합니다.
원인 복구 없는 clear()는 재시도를 고치지 않는다
상태 비트만 지우기 전에 권한·공간·장치 문제를 해결하거나 출력 대상을 바꿔야 합니다. 닫기 뒤 실패 상태도 반드시 보고합니다.
flush(), std::endl, close()는 C++ 스트림 버퍼를 연결된 파일 장치와 동기화하는 경계입니다. 운영체제·장치 캐시를 넘어 물리 디스크에 영속되었다는 보장은 별도입니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 도표 제목 | 호출 코드/ofstream/filebuf의 출력과 닫기 응답을 구분한다 |
| 관계·조건 설명 | 원래 가로 그림과 같은 세 참여자다. open 요청과 연결 응답, 값 삽입과 문자 전송, app일 때 filebuf의 끝 위치 이동, close 요청, 최종 동기화와 반환, close 반환, 상태 질의와 반환을 각 방향 화살표로 표시한다. 시간은 아래로 진행하고 요청·응답의 방향은 따로 읽는다. 버퍼 외부의 물리 디스크에서 정전 후 영속성을 보장하지는 않는다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 열기·삽입 호출 코드가 ofstream을 열고 값을 삽입한다. |
|
|
| 문자 전송 filebuf가 표현된 문자를 파일 쪽으로 전달한다. |
|
|
| close 확인 닫기와 최종 동기화 결과를 확인한다. |
|
|
ofstream 출력은 모드 선택부터 닫기 상태 확인까지 이어진다
호출 코드/ofstream/filebuf의 open·값 삽입·문자 전송·close 최종 동기화·상태 확인 메시지 순서가 명시적이다. 정전 영속성 보장으로 확대하지 않는다.
C++ · ofstream · sequence
쓰기는 close() 뒤 상태 확인까지 완료되어야 한다
모드가 기존 내용의 처리 방식을 정하고, 출력 연산은 먼저 스트림 버퍼를 거칩니다. 중요한 출력은 명시적으로 닫은 뒤 bad()와 fail()을 확인해야 마지막 동기화에서 드러난 오류까지 잡을 수 있습니다.
- 파일 정책을 먼저 고른다
out은 기본적으로 기존 내용을 교체하고,app은 매 쓰기 전에 파일 끝으로 이동합니다. - 연결 여부를 확인한다
is_open()은 파일 연결 여부를, 스트림의 불리언 상태는 현재 오류 비트를 확인합니다. - 기본 줄바꿈은
'\n'operator<<가 값을 문자로 서식화해 버퍼에 보내며, 필요한 경계에서만flush나endl을 사용합니다. - 중요한 출력은 명시적으로 닫는다
close()가 마지막 버퍼 동기화를 수행하게 해 소멸자 뒤에는 확인할 수 없는 오류를 앞에서 드러냅니다. - 닫은 뒤 상태를 판정한다
bad()는 심각한 I/O 오류를,fail()은failbit또는badbit를 보고합니다.
app은 매 쓰기, ate는 열 때 한 번
ate로 처음 끝에 놓인 뒤에는 위치를 바꿀 수 있지만, app은 각 쓰기 직전에 끝 위치를 강제합니다.
텍스트 변환을 끌 뿐 서식 출력을 바꾸지 않는다
binary여도 operator<<는 문자 표현을 만듭니다. 원시 바이트 블록은 write()로 보냅니다.
'\n'과 동기화 요구를 분리한다
보통은 '\n'으로 줄만 바꾸고, 외부 관찰이나 프로토콜 경계에서만 std::flush 또는 std::endl을 사용합니다.
원인 복구 없는 clear()는 재시도를 고치지 않는다
상태 비트만 지우기 전에 권한·공간·장치 문제를 해결하거나 출력 대상을 바꿔야 합니다. 닫기 뒤 실패 상태도 반드시 보고합니다.
flush(), std::endl, close()는 C++ 스트림 버퍼를 연결된 파일 장치와 동기화하는 경계입니다. 운영체제·장치 캐시를 넘어 물리 디스크에 영속되었다는 보장은 별도입니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 도표 제목 | 호출 코드/ofstream/filebuf의 출력과 닫기 응답을 구분한다 |
| 관계·조건 설명 | 원래 가로 그림과 같은 세 참여자다. open 요청과 연결 응답, 값 삽입과 문자 전송, app일 때 filebuf의 끝 위치 이동, close 요청, 최종 동기화와 반환, close 반환, 상태 질의와 반환을 각 방향 화살표로 표시한다. 시간은 아래로 진행하고 요청·응답의 방향은 따로 읽는다. 버퍼 외부의 물리 디스크에서 정전 후 영속성을 보장하지는 않는다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 열기·삽입 호출 코드가 ofstream을 열고 값을 삽입한다. |
|
|
| 문자 전송 filebuf가 표현된 문자를 파일 쪽으로 전달한다. |
|
|
| close 확인 닫기와 최종 동기화 결과를 확인한다. |
|
|
그림처럼 호출 코드가 <<로 넘긴 값은 글자로 바뀌어 파일 버퍼(filebuf)에 모이고, 버퍼가 그 글자를 파일 쪽으로 보냅니다. close()는 버퍼에 남은 글자를 마지막으로 보내고, 그 결과가 스트림의 상태 플래그에 남습니다. 그래서 쓰기는 close() 뒤에 상태를 확인할 때까지 끝난 것이 아닙니다.
std::ofstream으로 텍스트 파일 열기
std::ofstream도 객체를 만들면서 파일 이름을 넘기거나, 객체를 만든 뒤 open을 불러 엽니다. 아래 코드의 first는 생성자로, second는 open으로 연 경우입니다. first는 is_open()으로, second는 스트림을 조건에 그대로 넣어 실패를 확인하는데, 어느 쪽으로 확인해도 열기 실패를 알아낼 수 있습니다.
#include <fstream>
#include <iostream>
int main() {
std::ofstream first("output1.txt");
if (!first.is_open()) {
std::cerr << "cannot open output1.txt\n";
}
std::ofstream second;
second.open("output2.txt");
if (!second) {
std::cerr << "cannot open output2.txt\n";
}
}std::ofstream을 모드 없이 열면 쓰기 모드가 되며, 파일이 없으면 새로 만들고, 있으면 기존 내용을 모두 지운 뒤 처음부터 씁니다. 열기에 실패하면 failbit가 설정되므로 if (!second)로도 실패를 확인할 수 있습니다. 폴더가 없거나 쓰기 권한이 없는 위치라면 열기에 실패합니다.
<<로 쓰기
std::cout에 출력하듯이 <<로 문자열과 숫자를 씁니다. 숫자는 화면에 출력할 때와 같은 형식의 글자로 바뀌어 기록됩니다.
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
int main() {
std::ofstream out("my_data.txt");
if (!out.is_open()) {
std::cerr << "cannot open my_data.txt\n";
return 1;
}
out << "Hello, File World!\n";
out << "age: " << 30 << '\n';
out << "temperature: " << 25.7 << '\n';
std::string product = "Laptop";
out << product << ',' << 5 << ',' << 1200.50 << '\n';
out << std::fixed << std::setprecision(2)
<< product << ',' << 5 << ',' << 1200.50 << '\n';
out.close();
if (!out) {
std::cerr << "failed to finish writing my_data.txt\n";
return 1;
}
std::cout << "saved my_data.txt\n";
}실행하면 화면에는 saved my_data.txt가 출력되고, my_data.txt에는 다음 내용이 기록됩니다. 마지막 두 줄은 같은 값을 형식 설정 전과 후에 한 번씩 쓴 것이므로, 두 줄의 차이가 곧 std::fixed와 std::setprecision의 효과입니다.
Hello, File World!
age: 30
temperature: 25.7
Laptop,5,1200.5
Laptop,5,1200.50기본 형식은 의미 없는 끝자리 0을 빼므로 1200.50이 1200.5로 기록됩니다. 소수점 아래 자릿수를 고정하려면 형변환에서 본 std::fixed와 std::setprecision을 쓰며, 이 설정은 스트림에 남아 그 뒤의 실수 출력에도 계속 적용됩니다. 다른 프로그램이 읽을 파일이라면 이처럼 형식을 정해 두는 편이 안전합니다.
열기 모드: 지우고 쓰기와 이어 쓰기
기존 내용을 남긴 채 끝에 덧붙이려면 std::ios::app 모드로 엽니다. 실행 기록을 남기는 로그 파일이 대표적인 예입니다.
#include <fstream>
#include <iostream>
#include <string>
int main() {
{
std::ofstream out("modes.txt");
out << "A\n";
}
{
std::ofstream out("modes.txt");
out << "B\n";
}
{
std::ofstream out("modes.txt", std::ios::app);
out << "C\n";
}
std::ifstream in("modes.txt");
std::string line;
while (std::getline(in, line)) {
std::cout << line << '\n';
}
}B
C중괄호로 블록을 나누어, 스트림 세 개가 차례로 열리고 블록 끝에서 닫히게 했습니다. 둘째 블록의 기본 모드가 A를 지우고 B를 쓰며, 셋째 블록의 app 모드는 그 끝에 C를 덧붙입니다. 이 예제는 모드의 차이에 집중하려고 상태 확인을 생략했습니다.
C++ · output stream · diagnostic table
증상에서 모드·버퍼·상태 순으로 원인을 좁힌다
같은 operator<<라도 모드와 동기화 경계에 따라 결과가 달라집니다. 먼저 파일 수명 정책을 확인하고, 서식과 버퍼를 본 뒤, 쓰기와 close() 이후 상태로 완료 여부를 판정합니다.
| 증상·요구 | 먼저 확인 | 판정 | 조치·완료 확인 |
|---|---|---|---|
| 기존 내용이 사라짐 | out · trunc |
기본 ofstream(path) 출력은 기존 파일을 새 결과로 교체합니다. |
로그처럼 보존해야 한다면 app을 명시하고, 중복 기록 정책도 함께 정합니다. |
| 위치를 옮겨도 끝에 추가됨 | app 대 ate |
app은 매 쓰기 직전에 끝을 찾고, ate는 열 직후 한 번만 끝에 놓습니다. |
항상 추가할 때만 app을 쓰고, 이후 위치 변경이 필요하면 의도에 맞는 다른 모드 조합을 고릅니다. |
binary인데 숫자가 문자로 기록됨 |
binary · operator<< |
binary는 텍스트 변환을 끌 뿐, 삽입 연산의 숫자 서식화를 원시 바이트로 바꾸지 않습니다. |
문자 표현은 operator<<, 메모리의 바이트 블록은 크기와 형식을 정한 뒤 write()로 기록합니다. |
| 파일에 바로 보이지 않음 | '\n' · flush · endl |
'\n'은 줄만 바꾸며, flush와 endl은 C++ 스트림 버퍼 동기화를 요청합니다. |
기본 줄바꿈은 '\n'으로 두고, 외부 관찰 경계에서만 동기화합니다. 물리 디스크 영속성은 별도 수단이 필요합니다. |
| 열기 직후 출력할 수 없음 | is_open() · 스트림 상태 |
is_open()은 파일 연결 여부를, fail()은 failbit 또는 badbit 상태를 말합니다. |
경로와 권한을 고친 뒤 다시 열고, 연결되지 않은 스트림에는 쓰기를 시작하지 않습니다. |
| 쓰기 또는 닫기 실패 | 쓰기 후 상태 → close() → 상태 재확인 |
공간 부족·파일 시스템·장치 오류는 쓰기 도중이나 마지막 닫기에서 드러날 수 있습니다. fail()은 badbit도 포함합니다. |
중요한 출력은 명시적으로 닫고 다시 상태를 확인합니다. 원인을 복구하지 않은 clear()로 재시도하지 않습니다. |
| 줄·구분자·숫자 형식이 깨짐 | newline · separator · precision |
쓰기 코드와 읽기 코드가 서로 다른 형식 계약을 가정했을 수 있습니다. | 줄바꿈·구분자·정밀도를 한 형식으로 고정하고, 성공한 close() 뒤 결과를 소비자가 다시 읽어 검증합니다. |
기존 내용이 사라졌다면 out / trunc
기본 출력은 기존 파일을 새 결과로 교체합니다. 로그라면 app을 명시하고 중복 정책도 정합니다.
app은 매 쓰기, ate는 열 때 한 번
위치를 바꿔도 끝에 붙는다면 app의 매 쓰기 전 끝 이동이 원인입니다.
binary는 operator<<를 raw bytes로 바꾸지 않는다
문자 서식은 삽입 연산, 크기와 형식을 정한 바이트 블록은 write()로 기록합니다.
기본 줄바꿈은 '\n', 동기화는 의도한 경계만
flush와 endl은 스트림 버퍼를 동기화하지만 물리 디스크 영속성까지 보장하지 않습니다.
is_open()은 연결, fail()은 상태
경로와 권한을 고친 뒤 다시 열고, 연결되지 않은 스트림에는 출력을 시작하지 않습니다.
쓰기와 close() 뒤 상태를 모두 확인
bad()는 심각한 I/O 오류이며 fail()은 badbit도 포함합니다. 원인 복구 없는 clear()는 제외합니다.
newline · separator · precision
쓰기와 읽기가 같은 형식 계약을 쓰도록 고정하고, 성공한 close() 뒤 소비자가 다시 읽어 검증합니다.
열기 성공은 쓰기 성공이 아니다
쓰기 중 상태와 명시적 close() 뒤 상태가 모두 정상이어야 중요한 출력의 C++ 스트림 처리가 완료됩니다.
clear()는 원인을 제거하지 않는다
상태 비트만 지우기 전에 권한·공간·장치 문제를 해결하거나 다른 출력 대상으로 전환하는 명시적 복구가 필요합니다.
진단 순서는 “파일 수명 정책 → 연결 여부 → 서식과 버퍼 → 쓰기·닫기 상태”입니다. 이 순서를 지키면 내용 삭제, 지연된 관찰, 형식 불일치, 뒤늦은 I/O 실패를 서로 다른 원인으로 분리할 수 있습니다.
위 그림은 쓰기 결과가 예상과 다를 때 확인할 곳을 증상별로 정리한 것입니다. app은 매번 쓰기 직전에 파일 끝으로 가므로 중간 위치로 옮겨도 끝에 추가되고, ate는 연 직후 한 번만 끝으로 옮기므로 그 뒤의 위치는 바꿀 수 있습니다. std::ios::binary는 줄바꿈 변환을 끌 뿐이어서, binary로 열어도 형식화 출력 연산자(그림의 서식 출력)인 operator<<는 숫자를 여전히 글자로 기록하며, 바이트 그대로 쓰려면 다음 글의 write()를 씁니다.
출력 버퍼와 flush
파일 스트림은 쓸 때마다 파일에 직접 기록하지 않고, 버퍼에 모았다가 한꺼번에 보냅니다. 쓰기 요청을 자주 보내는 것보다 모아서 보내는 편이 훨씬 빠르기 때문입니다. 버퍼의 내용은 버퍼가 가득 찼을 때, flush()나 std::flush, std::endl을 썼을 때, 그리고 파일을 닫을 때 파일 쪽으로 보내집니다.
#include <fstream>
int main() {
std::ofstream out("progress.txt");
out << "step 1 done\n"; // 버퍼에 남아 있을 수 있음
out.flush(); // 지금까지의 내용을 파일 쪽으로 보냄
out << "step 2 done" << std::endl; // 줄바꿈과 flush를 함께
}'\n'은 줄바꿈만 넣고, std::endl은 줄바꿈을 넣은 뒤 flush까지 합니다. 백만 줄을 쓰는 루프에서 std::endl을 쓰면 줄마다 버퍼를 비워 눈에 띄게 느려지므로, 보통의 줄바꿈에는 '\n'을 쓰고 다른 프로그램이 지금 바로 읽어야 하는 지점에서만 flush합니다.
flush와 close가 보장하는 것은 C++ 스트림의 버퍼를 운영체제에 넘기는 것까지입니다. 운영체제나 저장 장치도 자기 캐시를 두므로, 넘긴 직후 전원이 꺼지면 내용이 사라질 수 있습니다. 정전에도 남아야 하는 데이터라면 운영체제가 제공하는 별도의 동기화 기능까지 확인해야 하며, 운영체제의 페이지 캐시와 fsync로 저장을 마치는 과정은 파일 시스템 실무에서 다룹니다.
더 깊이: 저장하다 멈춰도 옛 파일을 지키는 방법
운영체제의 캐시까지 저장 장치로 내보내는 함수는 플랫폼마다 따로 있습니다. POSIX에서는 fsync가, Windows에서는 FlushFileBuffers가 그 일을 하며, 둘 다 C++ 스트림이 아니라 운영체제의 파일 핸들을 받습니다. C++23까지의 파일 스트림은 그 핸들을 꺼내는 방법을 제공하지 않았지만, C++26은 std::basic_filebuf에 구현이 정하는 핸들을 돌려주는 native_handle()을 더했고 libstdc++는 GCC 14부터 이를 지원합니다. Microsoft 문서는 쓸 때마다 FlushFileBuffers를 부르면 비효율적일 수 있다고 적고 있으므로, 이런 호출은 저장을 마무리하는 지점에서 한 번 합니다.
기존 파일을 그 자리에서 고쳐 쓰다가 프로그램이 멈추면, 옛 내용도 새 내용도 온전하지 않은 파일이 남을 수 있습니다. 이를 피하는 흔한 방법은 같은 폴더의 임시 파일에 새 내용을 모두 쓰고, close() 뒤의 상태와 동기화까지 확인한 다음, std::filesystem::rename으로 임시 파일을 원래 이름으로 바꾸는 것입니다. 이 함수는 POSIX의 rename처럼 동작하도록 정해져 있어서, 그 이름의 파일이 이미 있으면 다른 프로세스가 그 이름이 사라진 순간을 보지 못하게 바꿉니다. 그래서 다른 프로그램은 바꾸기 전의 옛 파일과 다 쓴 새 파일 가운데 하나만 보게 됩니다.
쓰기 오류 확인
쓰기는 디스크가 가득 찼거나, 권한이 없거나, 장치에 문제가 생기면 실패할 수 있습니다. 실패는 상태 플래그에 남으므로 쓰기 뒤에, 그리고 마지막으로 close() 뒤에 확인합니다.
#include <fstream>
#include <iostream>
int main() {
std::ofstream out("report.txt");
if (!out.is_open()) {
std::cerr << "cannot open report.txt\n";
return 1;
}
out << "report line\n";
if (out.bad()) {
std::cerr << "serious write error\n";
return 1;
}
out.close();
if (!out) {
std::cerr << "failed to finish writing report.txt\n";
return 1;
}
}<< 직후에 성공해 보여도, 실제 기록은 버퍼에 남아 있다가 close()할 때 실패할 수 있습니다. 그래서 중요한 출력은 명시적으로 close()한 뒤 if (!out)으로 확인해야 마지막 기록의 실패까지 잡을 수 있습니다. 객체가 블록을 벗어나며 자동으로 닫힐 때도 같은 기록이 일어나지만, 그때의 실패는 확인할 방법이 없습니다.
실패했다고 clear()로 플래그만 지우면, 원인은 그대로 남아 다음 쓰기도 실패합니다. 쓰기에 실패했다면 원인을 알리고 끝내거나, 다른 위치에 쓰는 식으로 대처합니다.
정리
std::ofstream은 모드 없이 열면 파일을 새로 만들거나 기존 내용을 지우고 처음부터 씁니다.<<는 값을 글자로 바꿔 기록하며,std::fixed와std::setprecision같은 형식 설정은 스트림에 남습니다.- 기존 내용 뒤에 덧붙이려면
std::ios::app으로 열고,binary로 열어도<<는 글자로 기록합니다. - 출력은 버퍼에 모였다가 가득 차거나
flush,std::endl,close때 파일 쪽으로 보내지며, 보통의 줄바꿈에는'\n'을 씁니다. - 마지막 기록이 실패할 수 있으므로, 중요한 출력은
close()뒤 상태를 확인합니다. flush와close는 운영체제에 넘기는 것까지 보장하며, 정전에도 남는 것까지 보장하지는 않습니다.
연습 문제
1. 모드 하나 바꾸기
modes.cpp에서 둘째 블록의 스트림만 std::ios::app으로 열면 출력은 어떻게 되나요? 프로그램 전체를 두 번 실행하면 줄이 계속 늘어나나요?
해설 보기
첫 블록이 A를 쓰고, 둘째와 셋째 블록이 차례로 B와 C를 덧붙이므로 A, B, C의 세 줄이 출력됩니다. 프로그램을 다시 실행해도 첫 블록의 기본 모드가 기존 내용을 지우므로 다시 세 줄이며, 여섯 줄로 늘어나지 않습니다.
2. binary로 연 파일에 숫자 쓰기
std::ofstream out("number.dat", std::ios::binary); out << 258;을 실행하면 파일에는 몇 바이트가 기록되나요?
해설 보기
<<는 binary 모드에서도 숫자를 글자로 바꾸어 기록하므로, 파일에는 '2', '5', '8'의 3바이트가 기록됩니다. int의 4바이트를 그대로 쓰려면 <<가 아니라 다음 글에서 볼 write()를 써야 합니다.
3. 줄마다 std::endl
로그 파일에 백만 줄을 out << line << std::endl;로 쓰는 프로그램이 느립니다. 원인과 고치는 방법은 무엇인가요?
해설 보기
std::endl은 줄마다 버퍼를 비워, 백만 번의 기록 요청이 운영체제로 갑니다. out << line << '\n';으로 바꾸면 버퍼가 찰 때만 기록되어 요청 횟수가 크게 줄어듭니다. 중간 진행 상황을 다른 프로그램이 바로 봐야 한다면, 일정한 줄 수마다 한 번씩만 flush()합니다.
다음 글 바이너리 파일 입출력에서는 글자로 바꾸지 않고 바이트 그대로 읽고 쓰는 read()와 write(), 그리고 바이너리 형식을 정할 때 주의할 점을 다룹니다.