본문 바로가기

스트림과 파일 입출력 개요

스트림을 통한 데이터 흐름과 파일의 지속성을 이해하고 열기·읽기·쓰기·상태 확인·닫기의 기본 절차를 익힙니다.

지금까지 우리는 키보드로부터 데이터를 입력받고 (std::cin), 화면에 데이터를 출력하는 (std::cout) 콘솔 입출력을 주로 사용해 왔습니다.

하지만 프로그램의 데이터는 일회성으로 사용되는 경우가 많습니다.

프로그램이 종료되면 메모리에만 보관한 입력 데이터나 처리 결과는 사라집니다.

실제 응용 프로그램에서는 데이터를 영구적으로 저장하거나, 이전에 저장된 데이터를 불러와서 사용해야 할 필요가 있습니다.

이때 사용되는 것이 바로 파일 입출력(File I/O)입니다.

이 장에서는 C++에서 파일 입출력을 처리하는 기본 개념인 스트림(Stream)과 파일 입출력의 전체 구조를 정리합니다.

프로그램, 형식화 스트림, 스트림 버퍼, 파일과 외부 장치 사이의 입력·출력 방향을 보여 주는 C++ 스트림 구조

C++ · stream architecture · persistence

스트림 API는 버퍼를 거쳐 외부 자원과 값을 주고받는다

형식화는 스트림이, 실제 전송은 연결된 std::streambuf가 맡습니다. 파일은 표준 구성이고 네트워크는 별도 라이브러리나 플랫폼의 버퍼와 어댑터가 필요합니다.

입력과출력은서로반대인두별도경로desktop과같은두구획각4노드3간선이다.입력은외부→버퍼→istream→프로그램,출력은프로그램→ostream→버퍼→외부다.두구획을한실행순서로연결하지않는다.네트워크는별도버퍼구현이필요하고파일처리는기본표준구성이다.입력: 외부 → 프로그램외부 원천키보드·파일버퍼·어댑터filebuf / 외부 구현std::istream>> / getline프로그램 값메모리에서 사용출력: 프로그램 → 외부프로그램 값형식화할 데이터std::ostream<< / write버퍼·어댑터filebuf / 외부 구현외부 목적지화면·지속 파일
C++ 입력과 출력 스트림의 계층 구조 입력 행에서는 외부 원천에서 버퍼와 istream을 거쳐 프로그램으로 값이 들어오고, 출력 행에서는 프로그램에서 ostream과 버퍼를 거쳐 파일이나 외부 장치로 값이 나간다. INPUT · 외부 → 프로그램 외부 원천 키보드 · 파일 버퍼·어댑터 filebuf / external std::istream >> · getline 프로그램 값 메모리에서 사용 OUTPUT · 프로그램 → 외부 프로그램 값 형식화할 데이터 std::ostream << · write 버퍼·어댑터 filebuf / external 외부 목적지 화면 · 지속 파일
input

외부에서 프로그램으로

원천 → 버퍼·어댑터 → std::istream → 프로그램 순서로 읽습니다. 연결 상태와 읽기 결과를 각각 확인합니다.

output

프로그램에서 외부로

프로그램 → std::ostream → 버퍼·어댑터 → 파일 순서로 씁니다. 모드와 쓰기, 플러시 상태를 확인합니다.

both

양방향 파일 스트림

std::fstream은 std::iostream 계열 API와 파일 버퍼를 결합합니다. 방향과 모드, 읽기와 쓰기 위치를 함께 관리합니다.

network

네트워크는 별도 어댑터

표준 iostream에는 네트워크 스트림이 없습니다. 라이브러리나 플랫폼의 std::streambuf 구현이 필요합니다.

파일 스트림 클래스의 방향과 버퍼 연결
클래스형식화 API데이터 방향확인 책임
std::ifstreamstd::istream파일 → 프로그램연결 상태와 읽기 결과
std::ofstreamstd::ostream프로그램 → 파일모드와 쓰기·플러시 상태
std::fstreamstd::iostream파일 ↔ 프로그램방향·모드와 읽기/쓰기 위치

파일은 실행이 끝나도 값을 남기는 지속 장치입니다. 클래스를 고른 뒤에는 경로·모드·상태와 작업 결과를 확인합니다. 같은 API를 다른 자원에 재사용하려면 그 자원과 통신하는 버퍼나 어댑터가 있어야 합니다.


데이터 지속성 (Data Persistence)의 필요성

우리가 개발하는 프로그램은 종종 사용자 설정, 게임 진행 상황, 문서 내용, 이미지, 데이터베이스 정보 등 다양한 종류의 데이터를 다룹니다.

이러한 데이터가 프로그램 실행 중에만 존재하고 종료 시 사라진다면, 사용자는 매번 데이터를 다시 입력하거나 생성해야 하는 불편함을 겪게 됩니다.

데이터 지속성(Data Persistence)은 프로그램이 종료되더라도 데이터가 영구적으로 저장되어 다음 프로그램 실행 시에도 접근할 수 있도록 하는 능력입니다.

파일은 데이터를 지속적으로 저장하는 가장 기본적인 방법 중 하나입니다.


스트림 (Stream) 개념 이해하기

C++의 입출력은 스트림(Stream)이라는 추상적인 개념을 기반으로 합니다.

스트림은 데이터가 흐르는 통로(flow)를 의미합니다.

물이 흐르는 강물처럼 데이터도 한 곳에서 다른 곳으로 흘러간다고 생각할 수 있습니다.

  • 출력 스트림 (Output Stream): 데이터가 프로그램에서 외부(화면, 파일 등)로 흘러나가는 통로입니다. std::cout이 대표적인 출력 스트림입니다.
  • 입력 스트림 (Input Stream): 데이터가 외부(키보드, 파일 등)에서 프로그램으로 흘러들어오는 통로입니다. std::cin이 대표적인 입력 스트림입니다.

C++ 표준 라이브러리(iostream 및 fstream 헤더)는 이러한 스트림을 처리하기 위한 클래스들을 제공합니다.

  • std::istream: 모든 입력 스트림의 기본 클래스입니다. std::cin이 이 클래스의 객체입니다.
  • std::ostream: 모든 출력 스트림의 기본 클래스입니다. std::cout, std::cerr, std::clog가 이 클래스의 객체입니다.
  • std::fstream: 파일 입출력을 위한 스트림 클래스입니다.

스트림 개념 덕분에 프로그래머는 형식화 인터페이스(<<, >>, getline 등)를 파일과 콘솔에 일관되게 사용할 수 있습니다.

이 인터페이스를 네트워크에 적용하려면 네트워크와 통신하는 std::streambuf 구현이나 라이브러리·플랫폼 어댑터가 필요합니다. C++ 표준 라이브러리가 네트워크 스트림 자체를 제공하는 것은 아닙니다.

예를 들어, 화면에 출력하는 것과 파일에 출력하는 것은 사용법이 거의 유사합니다.


파일 입출력의 기본 단계

스트림과 파일 입출력은 열기 모드, 상태 플래그, 읽기·쓰기 단위, close 책임으로 나누어 확인합니다.

C++ 파일 스트림 선택과 상태 확인 흐름

선택한 하나의 방향/모드로 open→is_open→작업 결과를 확인하는 실제 연결 상태 절차이다. ifstream/ofstream/fstream 세 대안을 연속 실행하지 않는다.

읽기·쓰기 방향에 맞는 파일 스트림과 openmode를 고르고 연결 상태와 작업 결과를 구분해 확인하는 C++ 파일 입출력 흐름

C++ · file I/O · decision flow

방향을 먼저 고르고, 연결과 작업 상태를 따로 확인한다

클래스의 기본 방향과 명시한 openmode는 같은 규칙이 아닙니다. 특히 std::fstream에 모드를 넘기면 그 조합을 그대로 사용하므로 app만으로 in | out이 생기지 않습니다.

방향선택에서파일연결성공또는열기오류로분기한다방향선택→입력/출력/양방향세대안중하나→모드명시open→isopen.예는작업결과확인,아니요는열기오류다.세스트림종류를모두차례대로실행하지않는fan-out/fan-in이다.예경로가열기오류노드를거치지않는다.모바일에서도현재불리언상태와파일연관을구별한다.데이터 방향은?읽기 / 쓰기 / 양방향입력ifstream · mode|in출력ofstream · mode|out양방향fstream · mode그대로의도한 모드로 openout|app 등 명시is_open()?파일 연결 확인예작업 결과 확인읽기 성공 반복쓰기·닫기 상태아니요열기 오류경로·권한·상태
파일 스트림 선택부터 작업 결과 확인까지의 흐름 읽기, 쓰기, 양방향 중 하나를 선택해 ifstream, ofstream, fstream으로 나뉜다. 명시적 모드로 파일을 열고 is_open으로 연결을 확인한 뒤 성공한 경우 작업 결과를 조건으로 반복한다. 데이터 방향은? read · write · both read write both std::ifstream mode | in std::ofstream mode | out std::fstream mode 그대로 모드를 명시하고 open out | app · in | out | app is_open()? 파일 연결 여부 no 오류 처리 failbit · path · 권한 yes 읽기·쓰기 결과 확인 읽기: 성공 반복 · 쓰기: 상태 확인
  1. 방향 선택

    읽기는 ifstream, 쓰기는 ofstream, 양방향은 fstream을 고릅니다.

  2. 모드 명시

    fstream 추가 쓰기는 out | app, 양방향 추가는 in | out | app처럼 의도를 모두 씁니다.

  3. 연결 확인

    open() 뒤 is_open()으로 파일 연관 여부를 봅니다.

  4. 읽기·쓰기 결과 확인

    읽기는 추출·getline 성공으로 반복하고, 쓰기는 작업 후와 필요한 닫기 후 상태를 확인합니다.

  5. RAII 정리

    보통 소멸자가 닫습니다. 조기 해제나 닫기 오류 확인이 필요할 때만 close()를 명시합니다.

formal mode

클래스마다 모드 합성 규칙이 다르다

ifstream은 in, ofstream은 out을 합칩니다. fstream의 in | out은 모드를 생략했을 때의 기본값이며, 넘긴 모드에는 자동으로 붙지 않습니다.

state

연결, 정상 상태, EOF를 구분한다

is_open()은 파일 연관만, if (file)은 현재 failbit·badbit 부재를 봅니다. eof()는 eofbit 보고값이므로 읽기 루프 조건을 대신하지 않습니다.

formatted → line

>> 뒤 남은 줄바꿈을 처리한다

in >> number;
std::getline(in >> std::ws, line);

std::ws는 줄바꿈을 포함한 선행 공백을 제거하므로 그 동작이 의도에 맞을 때 사용합니다.

lifetime

정상 경로는 소멸자에 맡긴다

파일 스트림의 수명을 스코프로 제한하면 소멸 시 버퍼가 닫힙니다. 명시적 close()는 더 이른 경계나 오류 확인이라는 이유가 있을 때 사용합니다.

std::ofstream(path)의 기본 인수는 out입니다. 그 기본 호출의 out 단독 조합은 파일 버퍼에서 기존 내용을 비우는 쓰기 모드로 매핑되므로 이어 쓰기는 app을 명시합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
원 도표의 제목·노드·관계·조건·주석
범위원문
도표 제목방향 선택에서 파일 연결 성공 또는 열기 오류로 분기한다
관계·조건 설명방향 선택→입력/출력/양방향 세 대안 중 하나→모드 명시 open→is_open으로 이어진다. 예 분기는 작업 결과 확인, 아니요 분기는 열기 오류다. 세 스트림 종류를 모두 차례대로 실행하지 않는 fan-out/fan-in이다. 예 경로가 열기 오류 노드를 거치지 않는다. 모바일에서도 현재 불리언 상태와 파일 연관을 구별한다.
노드·연결선 라벨fstream · mode 그대로
본문·주석파일 스트림의 수명을 스코프로 제한하면 소멸 시 연결된 파일이 닫힙니다. 명시적
예의 값과 변화
관찰·상황값·보충 정보변화·이유
모드 선택

읽기/쓰기 중 이 사례의 방향·모드를 정한다.

  • 설정: 선택한 한 stream/모드
  • 상태: 아직 열리지 않음
  • ifstream/ofstream/fstream은 한 실행의 연속 단계가 아니다.
open 검사

연결 성공 여부를 is_open으로 확인한다.

  • 성공: 작업 가능
  • 실패: 작업 경로 진입하지 않음
  • 연결 실패를 성공한 I/O처럼 처리하지 않는다.
작업 결과

연결 성공 뒤 작업의 상태도 확인한다.

  • 연결: 성공 상태
  • 확인: 읽기/쓰기 결과
  • open 성공만으로 뒤의 모든 I/O 성공을 보장하지 않는다.

C++ 파일 스트림 선택과 상태 확인 흐름

선택한 하나의 방향/모드로 open→is_open→작업 결과를 확인하는 실제 연결 상태 절차이다. ifstream/ofstream/fstream 세 대안을 연속 실행하지 않는다.

읽기·쓰기 방향에 맞는 파일 스트림과 openmode를 고르고 연결 상태와 작업 결과를 구분해 확인하는 C++ 파일 입출력 흐름

C++ · file I/O · decision flow

방향을 먼저 고르고, 연결과 작업 상태를 따로 확인한다

클래스의 기본 방향과 명시한 openmode는 같은 규칙이 아닙니다. 특히 std::fstream에 모드를 넘기면 그 조합을 그대로 사용하므로 app만으로 in | out이 생기지 않습니다.

방향선택에서파일연결성공또는열기오류로분기한다방향선택→입력/출력/양방향세대안중하나→모드명시open→isopen.예는작업결과확인,아니요는열기오류다.세스트림종류를모두차례대로실행하지않는fan-out/fan-in이다.예경로가열기오류노드를거치지않는다.모바일에서도현재불리언상태와파일연관을구별한다.데이터 방향은?읽기 / 쓰기 / 양방향입력ifstream · mode|in출력ofstream · mode|out양방향fstream · mode그대로의도한 모드로 openout|app 등 명시is_open()?파일 연결 확인예작업 결과 확인읽기 성공 반복쓰기·닫기 상태아니요열기 오류경로·권한·상태
파일 스트림 선택부터 작업 결과 확인까지의 흐름 읽기, 쓰기, 양방향 중 하나를 선택해 ifstream, ofstream, fstream으로 나뉜다. 명시적 모드로 파일을 열고 is_open으로 연결을 확인한 뒤 성공한 경우 작업 결과를 조건으로 반복한다. 데이터 방향은? read · write · both read write both std::ifstream mode | in std::ofstream mode | out std::fstream mode 그대로 모드를 명시하고 open out | app · in | out | app is_open()? 파일 연결 여부 no 오류 처리 failbit · path · 권한 yes 읽기·쓰기 결과 확인 읽기: 성공 반복 · 쓰기: 상태 확인
  1. 방향 선택

    읽기는 ifstream, 쓰기는 ofstream, 양방향은 fstream을 고릅니다.

  2. 모드 명시

    fstream 추가 쓰기는 out | app, 양방향 추가는 in | out | app처럼 의도를 모두 씁니다.

  3. 연결 확인

    open() 뒤 is_open()으로 파일 연관 여부를 봅니다.

  4. 읽기·쓰기 결과 확인

    읽기는 추출·getline 성공으로 반복하고, 쓰기는 작업 후와 필요한 닫기 후 상태를 확인합니다.

  5. RAII 정리

    보통 소멸자가 닫습니다. 조기 해제나 닫기 오류 확인이 필요할 때만 close()를 명시합니다.

formal mode

클래스마다 모드 합성 규칙이 다르다

ifstream은 in, ofstream은 out을 합칩니다. fstream의 in | out은 모드를 생략했을 때의 기본값이며, 넘긴 모드에는 자동으로 붙지 않습니다.

state

연결, 정상 상태, EOF를 구분한다

is_open()은 파일 연관만, if (file)은 현재 failbit·badbit 부재를 봅니다. eof()는 eofbit 보고값이므로 읽기 루프 조건을 대신하지 않습니다.

formatted → line

>> 뒤 남은 줄바꿈을 처리한다

in >> number;
std::getline(in >> std::ws, line);

std::ws는 줄바꿈을 포함한 선행 공백을 제거하므로 그 동작이 의도에 맞을 때 사용합니다.

lifetime

정상 경로는 소멸자에 맡긴다

파일 스트림의 수명을 스코프로 제한하면 소멸 시 버퍼가 닫힙니다. 명시적 close()는 더 이른 경계나 오류 확인이라는 이유가 있을 때 사용합니다.

std::ofstream(path)의 기본 인수는 out입니다. 그 기본 호출의 out 단독 조합은 파일 버퍼에서 기존 내용을 비우는 쓰기 모드로 매핑되므로 이어 쓰기는 app을 명시합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.

관계도의 보충 설명

원 도표의 제목·노드·관계·조건·주석
범위원문
도표 제목방향 선택에서 파일 연결 성공 또는 열기 오류로 분기한다
관계·조건 설명방향 선택→입력/출력/양방향 세 대안 중 하나→모드 명시 open→is_open으로 이어진다. 예 분기는 작업 결과 확인, 아니요 분기는 열기 오류다. 세 스트림 종류를 모두 차례대로 실행하지 않는 fan-out/fan-in이다. 예 경로가 열기 오류 노드를 거치지 않는다. 모바일에서도 현재 불리언 상태와 파일 연관을 구별한다.
노드·연결선 라벨fstream · mode 그대로
본문·주석파일 스트림의 수명을 스코프로 제한하면 소멸 시 연결된 파일이 닫힙니다. 명시적
예의 값과 변화
관찰·상황값·보충 정보변화·이유
모드 선택

읽기/쓰기 중 이 사례의 방향·모드를 정한다.

  • 설정: 선택한 한 stream/모드
  • 상태: 아직 열리지 않음
  • ifstream/ofstream/fstream은 한 실행의 연속 단계가 아니다.
open 검사

연결 성공 여부를 is_open으로 확인한다.

  • 성공: 작업 가능
  • 실패: 작업 경로 진입하지 않음
  • 연결 실패를 성공한 I/O처럼 처리하지 않는다.
작업 결과

연결 성공 뒤 작업의 상태도 확인한다.

  • 연결: 성공 상태
  • 확인: 읽기/쓰기 결과
  • open 성공만으로 뒤의 모든 I/O 성공을 보장하지 않는다.

스트림 객체를 고를 때는 데이터의 방향과 파일을 다루는 의도를 먼저 분리해서 보면 실수가 줄어듭니다.

C++에서 파일을 사용하여 데이터를 저장하고 불러오는 과정은 다음과 같은 기본 단계를 따릅니다.

헤더 파일 포함: 파일 입출력을 위해 <fstream> 헤더 파일을 포함해야 합니다.

이 헤더는 파일 스트림 클래스들(std::ifstream, std::ofstream, std::fstream)을 정의합니다.

헤더 파일 포함
#include <fstream> // 파일 입출력 클래스들을 위해
#include <limits>  // ignore에 사용할 최대 스트림 크기를 위해

파일 스트림 객체 생성: 파일에서 읽거나 파일에 쓸 std::ifstream (입력 파일 스트림), std::ofstream (출력 파일 스트림), 또는 std::fstream (입력/출력 파일 스트림) 객체를 생성합니다.

파일 스트림 객체 생성
// 출력 파일 스트림 객체 (파일에 쓸 때)
std::ofstream outputFile;

// 입력 파일 스트림 객체 (파일에서 읽을 때)
std::ifstream inputFile;

// 입출력 파일 스트림 객체 (읽고 쓸 때)
std::fstream ioFile;

파일 열기 (Open): 생성된 스트림 객체를 특정 파일과 연결합니다.

파일 이름과 (필요하다면) 파일 열기 모드를 지정합니다.

파일 열기
outputFile.open("example.txt"); // 기본 인수 out, 기존 내용은 비움
inputFile.open("data.txt");     // 기본 인수 in
ioFile.open(
    "log.txt",
    std::ios::out | std::ios::app
); // fstream은 의도한 방향까지 명시

파일을 열 때는 다음의 std::ios_base::openmode 플래그를 조합하여 열기 모드를 지정할 수 있습니다.

모드 플래그방향과 동작
std::ios::in입력 시퀀스를 허용합니다.
std::ios::out출력 시퀀스를 허용합니다.
std::ios::app매 쓰기 전에 파일 끝으로 이동해 이어 씁니다.
std::ios::ate연 직후 위치를 파일 끝으로 옮기되 이후 위치 변경은 허용합니다.
std::ios::trunc파일을 열 때 기존 내용을 버립니다.
std::ios::binary구현의 텍스트 변환 없이 이진 모드로 엽니다.
  • std::ifstream의 생성자와 open은 기본 인수가 in이며, 전달한 모드에 in을 합쳐 파일 버퍼를 엽니다.
  • std::ofstream은 기본 인수가 out이며, 전달한 모드에 out을 합칩니다. 기본 호출로 만들어지는 out 단독 조합은 파일 버퍼에서 기존 내용을 비우는 쓰기 모드에 매핑되므로 이어 쓰려면 app을 명시합니다.
  • std::fstream은 모드를 생략했을 때만 in | out이 기본입니다. 모드를 직접 넘기면 그 값이 그대로 전달되므로 app만 넘겨도 in | out을 상속하지 않습니다. 출력 추가는 out | app, 양방향 추가는 in | out | app처럼 의도를 모두 씁니다.

바이너리 모드 실습은 다음 절 ‘바이너리 파일 입출력’에서 별도로 다룹니다.

파일 연결 확인: is_open()은 스트림이 파일과 연결되었는지만 확인합니다.

파일이 없거나 접근 권한이 없는 경우 열기 작업이 실패할 수 있습니다.

파일 열기 성공 여부 확인
if (outputFile.is_open()) {
    // 파일 열기 성공
} else {
    // 파일 열기 실패
    std::cerr << "파일을 열 수 없습니다!" << std::endl;
}

if (outputFile)은 열기 여부와 같은 질문이 아니라 현재 failbit·badbit가 없는지를 묻습니다. 작업 뒤 상태를 확인할 때 사용합니다. eof()는 eofbit가 설정됐다는 보고이므로, 읽기 루프는 while (std::getline(inputFile, line))처럼 작업 자체의 성공 결과를 조건으로 삼습니다.

파일 읽기/쓰기: 스트림 연산자(<<, >>)나 멤버 함수(read, write, getline 등)를 사용하여 파일에 데이터를 쓰거나 파일에서 데이터를 읽습니다.

파일 읽기/쓰기
// 파일에 쓰기 (출력 스트림)
outputFile << "Hello, File I/O!" << std::endl;
outputFile << 123 << " " << 45.67 << std::endl;

// 파일에서 읽기 (입력 스트림)
std::string line;
int number;
double value;
if (inputFile >> number >> value) { // 형식화 입력
    inputFile.ignore(
        std::numeric_limits<std::streamsize>::max(),
        '\n'
    ); // 남은 줄바꿈 제거
    std::getline(inputFile, line); // 다음 한 줄 읽기
}

형식화 입력 >>는 구분 문자를 스트림에 남길 수 있습니다. 이어서 getline을 호출할 때는 위처럼 줄바꿈을 ignore하거나, 선행 공백 제거가 의도에 맞다면 std::getline(inputFile >> std::ws, line)을 사용합니다.

수명과 닫기 (RAII): 파일 스트림 객체가 스코프를 벗어나 소멸되면 연결된 파일 버퍼도 닫힙니다.

정상 경로에서는 이 RAII 수명에 맡기는 편이 누락을 막습니다. 파일 연결을 더 일찍 끝내거나 close() 실패를 확인해야 할 때는 명시적으로 닫습니다.

파일 닫기
outputFile.close();
inputFile.close();

스트림 객체가 스코프를 벗어나 소멸될 때 (예: 함수 종료 시 지역 변수인 경우), 자동으로 close()가 호출됩니다.


간단한 파일 쓰기 예제

파일 쓰기 예제
#include <iostream> // 콘솔 출력을 위해
#include <fstream>  // 파일 입출력을 위해
#include <string>   // std::string을 위해

int main() {
    // 1. 출력 파일 스트림 객체 생성
    std::ofstream outFile;

    // 2. 파일 열기 (파일이 없으면 생성, 있으면 내용 지움)
    outFile.open("my_output.txt"); // 기본적으로 std::ios::out 모드

    // 3. 파일 열기 성공 여부 확인
    if (outFile.is_open()) {
        // 4. 파일에 데이터 쓰기
        outFile << "이것은 파일에 쓰여질 첫 번째 줄입니다.\n";
        outFile << "숫자: " << 12345 << std::endl;
        outFile << "이름: " << "홍길동" << std::endl;

        // 쓰기와 닫기 중에 발생한 오류까지 확인
        outFile.close();
        if (!outFile) {
            std::cerr << "오류: 'my_output.txt' 쓰기 또는 닫기를 완료하지 못했습니다.\n";
            return 1;
        }

        std::cout << "파일 'my_output.txt'에 성공적으로 기록했습니다." << std::endl;
    } else {
        std::cerr << "오류: 'my_output.txt' 파일을 열 수 없습니다!" << std::endl;
        return 1;
    }

    return 0;
}

쓰기와 닫기를 완료하면 프로세스의 현재 작업 디렉터리에 있는 my_output.txt에 위 내용이 기록되고, 성공 메시지가 출력됩니다. 오류가 발생해도 파일이 이미 생성되었거나 기존 내용이 지워졌을 수 있고, 새 내용은 일부만 기록될 수 있습니다. 버퍼 동기화와 오류 처리는 텍스트 파일 쓰기에서 이어집니다.


간단한 파일 읽기 예제

파일 입출력은 방향 선택, 모드 지정, 연결 확인, 작업 결과 확인, RAII 정리의 책임으로 나누어 볼 수 있습니다.

파일 읽기 예제
#include <iostream> // 콘솔 출력을 위해
#include <fstream>  // 파일 입출력을 위해
#include <string>   // std::string을 위해

int main() {
    // 1. 입력 파일 스트림 객체 생성
    std::ifstream inFile;

    // 2. 파일 열기
    inFile.open("my_output.txt"); // 위에서 생성한 파일을 읽기 모드로 엶

    // 3. 파일 열기 성공 여부 확인
    if (inFile.is_open()) {
        // 4. 파일에서 데이터 읽기
        std::string line;
        // 파일 끝까지 한 줄씩 읽기
        while (std::getline(inFile, line)) { // inFile에서 한 줄을 읽어 line 변수에 저장
            std::cout << line << std::endl;
        }

        if (inFile.bad()) {
            std::cerr << "파일을 읽는 중 I/O 오류가 발생했습니다." << std::endl;
            return 1;
        }

        std::cout << "\n파일 'my_output.txt'에서 성공적으로 읽었습니다." << std::endl;

        // 이 예제에서는 스코프가 끝날 때 RAII로 닫힌다.
    } else {
        std::cerr << "오류: 'my_output.txt' 파일을 열 수 없습니다! (파일이 없거나 권한 문제)" << std::endl;
    }

    return 0;
}

위 코드를 실행하면, my_output.txt 파일의 내용을 한 줄씩 읽어 콘솔에 출력합니다.

쓰기와 읽기를 두 번의 실행으로 연결하기

새 실습 폴더에서 앞의 파일 쓰기 프로그램을 write-text.cpp, 읽기 프로그램을 read-text.cpp로 따로 저장합니다. main이 둘이므로 한 소스에 붙이지 않습니다. GCC에서는 각각 g++ -std=c++20 -Wall -Wextra write-text.cpp -o write-text.exe, g++ -std=c++20 -Wall -Wextra read-text.cpp -o read-text.exe로 빌드하세요. Windows PowerShell에서는 .\write-text.exe 다음 .\read-text.exe, Linux 셸에서는 ./write-text.exe 다음 ./read-text.exe를 같은 폴더에서 실행합니다.

쓰기의 성공 메시지 뒤 파일에는 첫 번째 줄·숫자 12345·이름 홍길동의 세 줄이 있습니다. 읽기는 그 세 줄을 출력한 뒤 빈 줄과 읽기 완료 메시지를 출력합니다. 파일을 여는 상대 경로는 소스 위치나 실행 파일 위치가 아니라 프로세스의 현재 작업 폴더를 기준으로 합니다. 저장된 파일이 존재하는지와 다음 프로그램이 같은 경로를 여는지를 함께 확인하세요.

조건을 바꾸어 판단하기

실행 파일만 빈 다른 폴더로 복사하고, 그 폴더를 작업 폴더로 삼아 read-text를 실행했습니다. my_output.txt는 복사하지 않았습니다. 저장했던 세 줄이 보일까요? 원 읽기 프로그램의 종료 코드 0만으로 읽기가 성공했다고 판단할 수 있나요?

경로와 작업 성공의 풀이

그 폴더에서 파일을 찾을 수 없으므로 열기 오류 메시지가 나옵니다. 원 읽기 예제는 열기 실패 분기에서도 마지막 return 0;에 도달합니다. 이 예제를 자동 검사에 사용할 때는 실패 분기도 return 1; 등으로 구별하도록 별도로 바꾸어야 하며, 현재 원래 코드의 0만으로 성공을 판정하면 안 됩니다. 파일을 올바른 폴더에 복사하거나 의도한 작업 폴더로 돌아온 뒤 다시 읽습니다. 스트림 상태 정상은 전원 장애 뒤 영속성까지 확인한 결과는 아닙니다.

파일 입출력 코드는 스트림 종류보다 먼저 “어떤 방향으로 열고, 열기 성공을 확인했는가”를 점검하면 예외 상황을 훨씬 차분하게 다룰 수 있습니다.