Skip to content

21. C++ 입출력(C++ I/O)


1. 키워드

  • C++ 입출력(C++ I/O)
  • 스트림(Stream)과 버퍼(Buffer)
  • iostream 헤더 파일
  • istreamostream 클래스
  • 바이너리 파일(Binary File)과 텍스트 파일(Text File)
  • fstream 헤더 파일
  • ifstreamofstream 클래스
  • 파일 모드(File Mode)와 ios_base 클래스


2. 스트림과 버퍼

1) C++ 입출력

  • C++은 C와 마찬가지로 입출력에 관한 기능을 언어에서 기본적으로 제공하지 않는다.
  • 그 이유는 컴파일러를 만들 때 입출력 기능을 해당 하드웨어에 가장 적합한 형태로 만들 수 있도록 컴파일러 개발자에게 권한을 주기 위해서이다.
  • 하지만 대부분의 C++ 컴파일러는 iostreamfstream 헤더 파일에 정의되어 있는 클래스 라이브러리를 제공한다.
  • iostreamfstream 클래스 라이브러리의 중요 개념 중 하나가 바로 스트림이다.


2) 스트림

  • C++ 프로그램은 파일이나 콘솔의 입출력을 직접 다루지 않고, 스트림이라는 흐름을 통해 다룬다.
  • 스트림이란 실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름을 의미한다.
  • 즉, 스트림은 OS에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 한다.


001


3) 버퍼

  • 스트림은 내부에 버퍼라는 임시 메모리 공간을 가지고 있다.
  • 이러한 버퍼를 이용하면 입력과 출력을 좀 더 효율적으로 처리할 수 있게 된다.


002


  • 버퍼를 사용하면서 얻을 수 있는 장점은 다음과 같다.


1] 문자를 하나씩 전달하는 것이 아닌 묶어서 한 번에 전달하므로, 전송 시간이 적게 걸려 성능이 향상된다.

2] 사용자가 문자를 잘못 입력했을 경우 수정을 할 수가 있다.


  • 하지만 입력 작업에 버퍼를 사용하는 것이 반드시 좋은 것만은 아니다.
  • 빠른 반응이 요구되는 게임과 같은 프로그램에서는 키를 누르는 즉시 바로 전달되어야만 한다.


  • 이렇게 버퍼를 사용하는 입력과 버퍼를 사용하지 않는 입력은 서로 다른 용도로 사용된다.
  • 따라서 자신의 목적에 맞게 버퍼의 사용 여부를 판단해야 한다.
  • 참고로 대부분의 C++ 프로그램은 입력 시에는 사용자가 Enter 키를 누르면 입력 버퍼를 비우고, 출력 시에는 '\n'(개행 문자)를 전달 받으면 출력 버퍼를 비우게 된다.


3. 파일 입출력

1) 파일이란?

  • 파일이란 의미있는 정보를 담고 있으며, 이름을 가지고 있는 저장 장치 상의 논리적인 단위를 의미한다.
  • C++은 이러한 파일을 바이트별로 따로 읽을 수 있는 연속적인 바이트의 집합으로 취급한다.


2) 파일의 종류

  • 컴퓨터는 파일을 다음과 같이 두 가지 종류로 나누어서 다룬다.


1] 바이너리 파일

2] 텍스트 파일


  • 바이너리 파일은 데이터의 저장과 처리를 목적으로 01의 이진 형식으로 인코딩된 파일을 가리킨다.
  • 프로그램이 이 파일의 데이터를 읽거나 쓸 때는 데이터의 어떠한 변환도 일어나지 않는다.
  • 텍스트 파일은 사람이 알아볼 수 있는 문자열로 이루어진 파일을 가리킨다.
  • 프로그램이 이 파일의 데이터를 읽거나 쓸 때는 포맷 형식에 따라 데이터의 변환이 일어난다.


3) C++ 파일 입출력 클래스

  • C++에서 파일의 입출력을 표준 입출력처럼 처리한다.
  • C++ 표준 입출력은 iostream 헤더 파일의 istream 클래스와 ostream 클래스의 멤버 함수로 처리한다.
  • 이와 마찬가지로 C++ 파일 입출력은 fstream 헤더 파일의 클래스 라이브러리를 가지고 처리하게 된다.
  • 파일의 입력은 ifstream 클래스로, 파일의 출력은 ofstream 클래스의 멤버 함수로 처리한다.
  • 이 클래스는 iostream 헤더 파일의 클래스로부터 파생된 클래스이다.


4) 파일의 입출력

  • C++에서 파일에 대한 입출력 동작은 다음과 같은 순서에 따라 진행된다.


1] 스트림을 관리하기 위한 ifstream(또는 ofstream) 객체 생성

2] 특정 파일과의 연결

3] cin(또는 cout) 객체와 같은 방법으로 객체를 사용하여 입출력 수행

4] 모든 작업이 끝나면 파일과의 연결 종료


  • 특정 파일과의 연결은 open() 멤버 함수를 사용하여 다음과 같이 수행한다.


ifstream ifs;             // ifs라는 ifstream 객체를 생성함
ifs.open("example.txt");  // ifs를 example.txt와 연결함


  • 위에서 살펴본 ifstream 객체의 생성과 파일과의 연결을 다음과 같이 한 줄로 결합할 수도 있다.


ifstream ifs("example.txt");  // ifs라는 ifstream 객체를 생성하고, example.txt와 연결함


  • 이렇게 연결된 파일에서 cin 객체를 사용하는 방법과 동일한 방법으로 파일의 내용을 읽을 수 있다.


char ch;
char buf[20];
string str;

ifs >> ch;             // example.txt 파일에서 한 문자를 읽어 변수 ch에 저장함
ifs.getline(buf, 20);  // example.txt 파일에서 한 행을 읽어 배열 buf에 저장함
getline(ifs, str);     // example.txt 파일에서 한 행을 읽어 문자열 객체인 str에 저장함


  • 앞서 생성한 입출력 파일 스트림 객체의 수명이 다하면 파일은 자동으로 닫힌다.
  • 또한, close() 멤버 함수를 통해 명시적으로 파일과의 연결을 닫을 수도 있다.


ifs.close();  // 파일과의 연결을 닫음


5) 스트림의 상태 검사

  • C++의 파일 입출력 클래스는 스트림의 상태를 검사해 주는 ios_base 클래스의 멤버 함수를 상속 받는다.


  • 과거의 C++에서는 파일을 성공적으로 열었는지를 다음과 같은 방법으로 확인했다.


1. if (ifs.fail()) { ... }   // 파일을 여는데 실패한 경우
2. if (!ifs.good()) { ... }  // 파일을 여는데 실패한 경우
3. if (!ifs) { ... }         // 파일을 여는데 실패한 경우


  • 최신의 C++에서는 파일의 성공적인 개방을 is_open 멤버 함수를 통해 확인할 수도 있다.


if (!ifs.is_open()) { ... }  // 파일을 여는데 실패한 경우


  • 다음의 코드는 C++에서 단순히 파일을 열어 그 안에 저장된 텍스트 한 줄을 읽어 오는 것이다.


#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main(void) {
    ifstream ifs;
    string str;

    ifs.open("example.txt");

    if (!ifs.is_open()) {
        cout << "파일을 열 수가 없습니다!" << endl;
        exit(1);
    } else {
        cout << "파일을 성공적으로 열었습니다!" << endl;
        getline(ifs, str);
        cout << str << endl;
        ifs.close();
    }

    return 0;
}

// 파일을 성공적으로 열었습니다!
// C++ 프로그래밍


4. 파일 모드

  • C++에서 파일 모드란 파일의 사용 용도와 파일의 데이터를 어떤 방식으로 입출력할지를 결정하는 상수이다.
  • 파일 모드 상수는 ifstream(또는 ofstream) 객체를 파일의 이름으로 초기화하거나, open() 멤버 함수를 통해 스트림에 파일을 연결할 때 파일 모드를 지정하기 위한 두 번째 매개변수로 사용된다.


// ifs라는 ifstream 객체를 생성하고, example.txt와 읽기 모드로 연결함
ifstream ifs("example.txt", ios_base::in);


  • 기본적으로 ifstream의 생성자와 open() 멤버 함수는 파일 모드의 디폴트 인수로 ios_base::in을 제공한다.
  • 또한, ofstream의 생성자와 open() 멤버 함수는 파일 모드의 디폴트 인수로 ios_base::out | ios_base::trunc를 제공한다.


1) C++ 파일 모드 상수

  • C++에서 제공하는 파일 모드 상수는 다음과 같다.


1] ios_base::in

  • 파일을 오로지 읽는 것만 가능한 모드로 개방함

2] ios_base::out

  • 파일을 쓰는 것만이 가능한 모드로 개방함

3] ios_base::ate

  • 파일을 개방할 때 파일의 끝으로(At The End) 파일 포인터를 이동시킴

4] ios_base::app

  • 해당 파일의 맨 끝에서부터 데이터를 추가함(Append)

5] ios_base::trunc

  • 파일이 있으면 해당 파일의 모든 데이터를 지우고(Truncate) 개방함

6] ios_base::binary

  • 바이너리 모드로 개방함


  • 이렇게 제공되는 파일 모드 상수는 단독으로 사용할 수도 있고, 여러 모드를 조합하여 사용할 수도 있다.
  • C++에서 자주 사용되는 파일 모드 조합은 다음과 같다.


1] ios_base::out | ios_base::trunc

  • C 파일 모드: "w"
  • 파일을 쓰는 것만이 가능한 모드로 개방함
  • 파일이 없으면 새 파일을 만들고, 파일이 있으면 해당 파일의 모든 데이터를 지우고 개방함

2] ios_base::out | ios_base::app

  • C 파일 모드: "a"
  • 파일을 쓰는 것만이 가능한 모드로 개방함
  • 파일이 없으면 새 파일을 만들고, 파일이 있으면 해당 파일의 맨 끝에서부터 데이터를 추가함

3] ios_base::in | ios_base::out

  • C 파일 모드: "r+"
  • 파일을 읽고 쓰는 것이 둘 다 가능한 모드로 개방함

4] ios_base::in | ios_base::out | ios_base::trunc

  • C 파일 모드: "w+"
  • 파일을 읽고 쓰는 것이 둘 다 가능한 모드로 개방함
  • 파일이 없으면 새 파일을 만들고, 파일이 있으면 해당 파일의 모든 데이터를 지우고 개방함

References