C++의 기본적인 파일 입출력을 넘어, 실무에서는 데이터의 크기를 줄이기 위한 바이너리 처리나 대용량 파일의 특정 지점으로 바로 이동하는 파일 포인터 제어 기술이 필수적입니다.
오늘은 텍스트 파일 처리를 넘어선 C++ 파일 입출력의 심화 주제들을 살펴보고, 성능과 안정성을 모두 잡는 구현 방법을 알아보겠습니다.

1. 바이너리(Binary) 파일 읽기 및 쓰기
바이너리 파일은 데이터를 문자열로 변환하지 않고 메모리 형태 그대로 저장합니다. 텍스트 파일보다 용량이 작고 처리 속도가 압도적으로 빠르다는 장점이 있습니다.
바이너리 쓰기 및 읽기 예제
구조체(struct) 데이터를 한 번에 저장하고 불러오는 방식은 설정 값이나 게임 데이터를 관리할 때 매우 유용합니다.
#include <iostream>
#include <fstream>
struct UserData {
char name[50];
int level;
float gold;
};
int main() {
UserData saveTarget = {"Hero", 99, 1500.5f};
// 1. 바이너리 쓰기 (ios::binary 모드 필수)
std::ofstream outFile("save.dat", std::ios::binary);
if (outFile.is_open()) {
outFile.write(reinterpret_cast<char*>(&saveTarget), sizeof(saveTarget));
outFile.close();
}
// 2. 바이너리 읽기
UserData loadData;
std::ifstream inFile("save.dat", std::ios::binary);
if (inFile.is_open()) {
inFile.read(reinterpret_cast<char*>(&loadData), sizeof(loadData));
std::cout << "불러온 이름: " << loadData.name << ", 레벨: " << loadData.level << std::endl;
inFile.close();
}
return 0;
}
2. 파일 포인터 위치 제어 (seekg, seekp)
파일의 처음부터 끝까지 읽는 것이 아니라, 특정 위치로 점프하여 데이터를 읽거나 수정해야 할 때 파일 포인터를 사용합니다.
- seekg (seek get): 읽기 포인터(ifstream) 위치 이동
- seekp (seek put): 쓰기 포인터(ofstream) 위치 이동
- tellg / tellp: 현재 포인터의 위치 반환
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("config.txt");
// 파일 끝으로 이동하여 파일 크기 확인하기
file.seekg(0, std::ios::end);
int fileSize = file.tellg();
std::cout << "파일 전체 크기: " << fileSize << " 바이트" << std::endl;
// 다시 시작 지점에서 10바이트 뒤로 이동
file.seekg(10, std::ios::beg);
// 이후 데이터 읽기 수행...
return 0;
}
3. 파일 스트림 상태 확인 (Safe I/O)
파일을 읽을 때 발생할 수 있는 다양한 오류 상황을 체크하는 것은 프로그램의 안정성을 결정짓습니다.
- good(): 스트림에 아무 문제가 없음
- eof(): 파일 끝(End of File)에 도달함
- fail(): 논리적 오류(잘못된 타입 읽기 등) 발생
- bad(): 물리적인 읽기/쓰기 오류 등 심각한 상황
if (inFile.fail()) {
std::cerr << "데이터 타입 불일치 또는 파일 열기 실패!" << std::endl;
}
4. 실전 활용: 특정 키워드 추출 (로그 분석 도구)
수천 줄의 로그 파일에서 특정 단어(예: "Error")가 포함된 줄만 추출하는 기능은 개발자에게 매우 유용합니다.
#include <string>
#include <fstream>
void findErrorInLog(const std::string& filename) {
std::ifstream logFile(filename);
std::string line;
while (std::getline(logFile, line)) {
if (line.find("[ERROR]") != std::string::npos) {
std::cout << "심각한 오류 로그: " << line << std::endl;
}
}
}
5. 파일 입출력과 예외 처리 (Exception)
C++의 try-catch를 활용하면 파일 열기 실패나 읽기 도중의 갑작스러운 중단을 우아하게 처리할 수 있습니다.
#include <stdexcept>
int main() {
try {
std::ifstream file("secret.txt");
file.exceptions(std::ifstream::failbit | std::ifstream::badbit); // 예외 발생 설정
// 파일 처리 로직...
} catch (const std::ios_base::failure& e) {
std::cerr << "파일 작업 중 예외 발생: " << e.what() << std::endl;
}
return 0;
}
결론: 효율적인 데이터 관리를 위하여
C++ 파일 입출력 심화 기법을 익히면 단순한 텍스트 저장을 넘어, 고성능 데이터베이스나 로그 분석기, 게임 세이브 시스템을 구축할 수 있습니다. 특히 바이너리 처리와 포인터 제어는 임베디드 및 시스템 프로그래밍에서 리소스를 최적화하는 핵심 기술입니다.
제시해 드린 예제를 바탕으로 여러분만의 파일 관리 모듈을 설계해 보세요!
'Core Programming > Modern C++ & System Design' 카테고리의 다른 글
| C++ 멀티스레드 프로그래밍 완벽 가이드: thread, mutex, condition_variable (0) | 2024.12.21 |
|---|---|
| C++ 예외 처리 완벽 가이드: try-catch-throw 문법과 실전 예제 (0) | 2024.12.20 |
| C++ STL 총정리: 컨테이너, 반복자, 알고리즘 핵심 요약 및 예제 (0) | 2024.12.20 |
| C++ 템플릿(Template) 완벽 정리: 함수·클래스 템플릿부터 특수화까지 (0) | 2024.12.20 |
| C++ 네임스페이스(Namespace) 사용법 총정리: 이름 충돌 방지와 모듈화 (0) | 2024.12.20 |