메뉴 닫기

C++ 바이너리 파일 처리 방법: ios::binary와 구조체, 이미지까지!


C++ 바이너리 파일 처리 방법: ios::binary와 구조체, 이미지까지!

💡 바이너리 모드로 파일을 다루는 법, C++로 완벽히 이해해보세요

안녕하세요!
개발을 하다 보면 텍스트 파일 외에도 이미지나 실행파일처럼 바이너리 데이터를 직접 다뤄야 할 때가 있습니다.
특히 C++에서는 텍스트 스트림과는 전혀 다른 방식으로 접근해야 하는데요.
처음 접하신다면 구조체를 통째로 저장하거나 불러오는 게 과연 가능할지 궁금하셨을 거예요.
오늘은 ios::binary 모드를 사용한 C++ 바이너리 파일 처리 방법을 친절하게 소개드릴게요.
텍스트로는 표현되지 않는 이진 데이터의 세계, 어렵지 않게 설명드릴 테니 함께 따라와 주세요.

이번 글에서는 C++에서 구조체나 이미지 파일 같은 바이너리 데이터를 어떻게 읽고 쓰는지를 구체적인 예제와 함께 소개합니다.
또한 fread/fwrite와 같은 C 방식과의 차이점도 비교하며 이해를 돕고, 실무에서도 자주 쓰이는 활용법까지 안내해 드릴게요.
C++ 입문자부터 중급 개발자까지 모두가 이해할 수 있도록 구성했으니, 끝까지 읽으시면 분명 도움이 되실 겁니다!







🧾 바이너리 파일이란?

바이너리 파일(Binary File)이란 사람이 읽을 수 있는 텍스트가 아닌 0과 1의 이진 데이터로 구성된 파일입니다.
우리가 흔히 다루는 텍스트 파일(.txt)은 글자 그대로의 데이터를 저장하는 반면, 바이너리 파일은 숫자, 이미지, 오디오, 실행코드 등 다양한 형식의 정보를 압축된 형태로 담고 있어요.

대표적인 예로는 다음과 같은 파일들이 있습니다.

  • 📸JPG, PNG와 같은 이미지 파일
  • 🎵MP3, WAV 등의 오디오 파일
  • 🧠컴퓨터가 처리하는 실행 파일(.exe)

이처럼 바이너리 파일은 고속 처리와 용량 절약 측면에서 효율적이지만, 텍스트 편집기로 열면 제대로 표시되지 않거나 깨져 보입니다.
그렇기 때문에 전용 프로그램이나 바이너리 스트림을 다룰 수 있는 코드로 접근해야 합니다.

C++에서는 이러한 파일을 읽고 쓰기 위해 특별한 모드가 필요한데, 그게 바로 ios::binary입니다.
이 모드를 사용하면 텍스트 해석 없이 있는 그대로의 바이트 데이터를 처리할 수 있어요.

💬 텍스트 파일과는 달리, 바이너리 파일은 데이터를 바이트 단위로 직접 제어해야 합니다.

이제 다음 단계에서는 C++ 코드에서 ios::binary를 실제로 어떻게 사용하는지 예제와 함께 살펴보겠습니다.


📂 C++에서 ios::binary 사용하는 법

C++에서는 파일 스트림 객체를 사용할 때 ios::binary 플래그를 지정함으로써 바이너리 모드로 파일을 읽거나 쓸 수 있습니다.
기본적으로 ofstream, ifstream, fstream 클래스에서 이 옵션을 함께 지정해주면 됩니다.

  • ✏️ofstream → 파일에 바이너리 데이터 쓰기
  • 📖ifstream → 파일에서 바이너리 데이터 읽기
  • 🔀fstream → 읽기/쓰기 겸용 스트림

사용법은 아래 예제처럼 간단합니다.

CODE BLOCK
#include <fstream>

int main() {
    std::ofstream outFile("data.bin", std::ios::binary);
    int number = 12345;
    outFile.write(reinterpret_cast<char*>(&number), sizeof(number));
    outFile.close();

    std::ifstream inFile("data.bin", std::ios::binary);
    int readNumber;
    inFile.read(reinterpret_cast<char*>(&readNumber), sizeof(readNumber));
    inFile.close();

    std::cout << "읽은 값: " << readNumber << std::endl;
    return 0;
}

위 코드에서는 정수형 데이터를 바이너리 파일로 저장한 뒤 다시 읽어오는 예제를 보여주고 있어요.
텍스트로 저장되었을 때보다 훨씬 간단하고 빠르게 데이터를 처리할 수 있는 장점이 있습니다.

💡 TIP: write/read 함수는 첫 번째 인자로 char* 타입을 요구하므로 reinterpret_cast를 꼭 사용해줘야 합니다.

다음 단계에서는 바이너리 모드의 진가를 제대로 느낄 수 있는 구조체 파일 저장과 읽기 예제를 살펴보겠습니다.







📦 구조체를 파일에 저장하고 불러오기

C++에서는 구조체(struct)를 바이너리 파일로 저장하고, 다시 불러오는 작업도 매우 간단합니다.
이때도 역시 ios::binary 모드write(), read() 함수를 활용하게 되는데요.
실제 메모리 상의 구조를 그대로 저장하므로, 텍스트 형식보다 훨씬 빠르고 효율적입니다.

아래는 예제를 통해 구조체를 어떻게 저장하고 다시 불러오는지 보여드릴게요.

CODE BLOCK
#include <iostream>
#include <fstream>

struct Person {
    char name[20];
    int age;
};

int main() {
    Person p1 = {"Alice", 30};

    std::ofstream outFile("person.dat", std::ios::binary);
    outFile.write(reinterpret_cast<char*>(&p1), sizeof(p1));
    outFile.close();

    Person p2;
    std::ifstream inFile("person.dat", std::ios::binary);
    inFile.read(reinterpret_cast<char*>(&p2), sizeof(p2));
    inFile.close();

    std::cout << "이름: " << p2.name << ", 나이: " << p2.age << std::endl;
    return 0;
}

이 예제에서 Person 구조체를 그대로 파일에 저장하고, 다시 읽어 출력하고 있습니다.
바이너리 방식이기 때문에 텍스트 파싱 없이도 메모리 내용을 그대로 복원할 수 있어요.

⚠️ 주의: 구조체에 포인터 멤버가 있을 경우, 포인터 자체 주소만 저장되므로 복원 시 올바른 데이터가 아닙니다.
포인터 대신 배열 또는 고정 길이 데이터를 사용하세요.

또한 구조체의 패딩(padding) 문제로 인해 예상치 못한 결과가 생길 수 있으니, #pragma pack을 활용하거나 데이터를 정렬해서 쓰는 것도 고려해보세요.

다음 단계에서는 이미지와 같은 복잡한 바이너리 파일을 다루는 방법도 살펴보겠습니다.


🖼️ 이미지와 바이너리 파일 다루기

이미지 파일은 대표적인 바이너리 데이터입니다.
텍스트처럼 직접 편집할 수 없고, 특정 포맷(JPG, PNG 등)에 따라 저장된 바이너리 정보를 프로그램이 해석해 보여주는 방식이죠.
C++에서도 이러한 이미지 파일을 그대로 복사하거나 특정 바이트를 분석하고 싶을 때 바이너리 모드를 활용할 수 있습니다.

아래 예제는 이미지 파일을 그대로 읽어와 새로운 파일로 복사하는 간단한 코드입니다.

CODE BLOCK
#include <fstream>

int main() {
    std::ifstream inFile("image.jpg", std::ios::binary);
    std::ofstream outFile("copy.jpg", std::ios::binary);

    if (inFile && outFile) {
        outFile << inFile.rdbuf(); // 전체 파일 복사
    }

    inFile.close();
    outFile.close();

    return 0;
}

이 코드는 이미지 내용을 텍스트로 해석하지 않고 그대로 복사하기 때문에, 원본과 완전히 동일한 결과를 얻을 수 있습니다.
이처럼 rdbuf()를 사용하면 바이너리 데이터를 빠르게 복제할 수 있어요.

💎 핵심 포인트:
이미지 포맷에 따라 바이너리 구조가 다르므로, 분석 또는 가공 목적이라면 포맷 명세(JPEG 헤더, PNG 시그니처 등)를 미리 공부해두면 좋아요.

또한 파일의 헤더 정보만 부분적으로 읽어서 확장자나 형식을 감지하는 작업도 가능합니다.
이런 방식은 보안 검증, 파일 분류 등의 용도로도 자주 사용돼요.

이제 다음 섹션에서는 많은 분들이 궁금해하는 C 언어의 fread, fwrite 함수와 C++의 차이점을 비교해보겠습니다.







🧪 fread/fwrite와의 차이점 비교

바이너리 파일을 다룰 때 C언어에서는 fread(), fwrite()를 자주 사용하고, C++에서는 read(), write()를 사용합니다.
기능은 유사하지만, 사용 방식과 안전성 측면에서 차이가 존재해요.

  • 📘C 방식: FILE 포인터 사용, C 표준 라이브러리 의존
  • 📗C++ 방식: 스트림 객체 기반, 예외 처리와 캡슐화에 강함

각각의 방식은 아래와 같은 코드로 구현됩니다.

CODE BLOCK
// C 스타일
#include <stdio.h>

int main() {
    FILE* fp = fopen("data.bin", "wb");
    int num = 42;
    fwrite(&num, sizeof(int), 1, fp);
    fclose(fp);

    fp = fopen("data.bin", "rb");
    int readNum;
    fread(&readNum, sizeof(int), 1, fp);
    fclose(fp);

    printf("읽은 값: %d\n", readNum);
    return 0;
}

CODE BLOCK
// C++ 스타일
#include <fstream>
#include <iostream>

int main() {
    int num = 42;
    std::ofstream out("data.bin", std::ios::binary);
    out.write(reinterpret_cast<char*>(&num), sizeof(num));
    out.close();

    int readNum;
    std::ifstream in("data.bin", std::ios::binary);
    in.read(reinterpret_cast<char*>(&readNum), sizeof(readNum));
    in.close();

    std::cout << "읽은 값: " << readNum << std::endl;
    return 0;
}

C 방식은 간단하지만 버퍼 관리와 에러 처리를 직접 해줘야 하고, 포인터 사용에 따른 위험성도 존재합니다.
반면, C++ 방식은 객체지향적인 장점과 함께 예외 처리, 자동 리소스 관리(RAII) 등이 가능해 실무에서는 더 많이 권장되는 방법이에요.

💡 TIP: 성능 차이는 미미하지만, 유지보수성과 안정성을 고려하면 C++ 방식의 스트림 입출력이 더 유리합니다.

이제 바이너리 입출력에 대한 핵심 내용은 모두 다뤘고, 마지막으로 자주 묻는 질문(FAQ)에서 궁금한 부분을 정리해드릴게요.


❓ 자주 묻는 질문 (FAQ)

바이너리 파일과 텍스트 파일의 가장 큰 차이점은 뭔가요?
텍스트 파일은 사람이 읽을 수 있는 문자열 데이터를 저장하고, 바이너리 파일은 컴퓨터가 직접 처리하는 이진 데이터를 저장합니다.
저장 방식과 해석 방식이 완전히 다릅니다.
ios::binary 모드는 언제 꼭 써야 하나요?
텍스트가 아닌 이미지, 오디오, 구조체, 실행파일 등 이진 데이터를 정확히 처리하려면 반드시 ios::binary 모드를 사용해야 합니다.
구조체 저장 시 포인터가 들어 있으면 안 되나요?
네, 포인터는 주소값만 저장되기 때문에 복원 시 유효한 데이터가 아닐 수 있어요.
실제 데이터를 저장하려면 배열을 쓰는 것이 안전합니다.
이미지 파일은 직접 열어 처리할 수 있나요?
직접 열 수는 있지만 포맷 구조를 이해하지 않으면 의미 있는 처리가 어렵습니다.
PNG, JPG 등은 각자 고유한 헤더와 데이터 구조를 갖고 있어요.
rdbuf()는 어떤 역할을 하나요?
rdbuf()는 입력 스트림 버퍼 전체를 통째로 출력 스트림으로 복사할 때 사용합니다.
특히 바이너리 파일 복사에 유용합니다.
read/write 함수는 문자열에도 쓸 수 있나요?
가능합니다.
단, C++의 std::string이 아닌 C 스타일 문자열(char 배열)을 대상으로 하며, 길이를 정확히 지정해줘야 합니다.
파일 크기 측정은 어떻게 하나요?
fseek/ftell 또는 C++의 seekg/tellg를 사용하여 파일의 현재 위치를 측정해 크기를 구할 수 있습니다.
C 스타일 fread/write 방식은 써도 괜찮나요?
기능적으로는 문제없지만, 현대 C++에서는 RAII와 예외 처리가 가능한 fstream을 쓰는 것이 유지보수나 안정성 측면에서 더 유리합니다.



📌 바이너리 파일 입출력 마무리 정리

이번 글에서는 C++에서 ios::binary 모드를 활용한 바이너리 파일 처리 방법을 단계별로 살펴보았습니다.
텍스트가 아닌 이진 데이터를 다루는 법부터 구조체를 저장하고 복원하는 실전 예제, 이미지 파일 복사, 그리고 C 방식인 fread/fwrite와의 차이점까지 다양한 주제를 다뤘습니다.
바이너리 입출력은 단순한 파일 저장을 넘어서, 실행 파일 생성, 네트워크 통신, 데이터 직렬화 등 고급 개발에도 활용되므로 정확한 개념 이해가 중요합니다.
앞으로 파일 처리나 데이터를 다룰 때 이 내용을 기반으로 활용해보세요.
복잡해 보일 수 있지만, 실제 코드를 따라해 보면 금방 익숙해지실 거예요.


🏷️ 관련 태그:바이너리파일, C++입출력, 구조체저장, 이미지복사, iosbinary, fstream, freadfwrite, 파일처리기초, C프로그래밍, 개발팁