21. C++ 입출력(C++ I/O)
1. 키워드
- C++ 입출력(C++ I/O)
- 스트림(Stream)과 버퍼(Buffer)
iostream
헤더 파일istream
과ostream
클래스- 바이너리 파일(Binary File)과 텍스트 파일(Text File)
fstream
헤더 파일ifstream
과ofstream
클래스- 파일 모드(File Mode)와
ios_base
클래스
2. 스트림과 버퍼
1) C++ 입출력
- C++은 C와 마찬가지로 입출력에 관한 기능을 언어에서 기본적으로 제공하지 않는다.
- 그 이유는 컴파일러를 만들 때 입출력 기능을 해당 하드웨어에 가장 적합한 형태로 만들 수 있도록 컴파일러 개발자에게 권한을 주기 위해서이다.
- 하지만 대부분의 C++ 컴파일러는
iostream
과fstream
헤더 파일에 정의되어 있는 클래스 라이브러리를 제공한다. iostream
과fstream
클래스 라이브러리의 중요 개념 중 하나가 바로 스트림이다.
2) 스트림
- C++ 프로그램은 파일이나 콘솔의 입출력을 직접 다루지 않고, 스트림이라는 흐름을 통해 다룬다.
- 스트림이란 실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름을 의미한다.
- 즉, 스트림은 OS에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 한다.
3) 버퍼
- 스트림은 내부에 버퍼라는 임시 메모리 공간을 가지고 있다.
- 이러한 버퍼를 이용하면 입력과 출력을 좀 더 효율적으로 처리할 수 있게 된다.
- 버퍼를 사용하면서 얻을 수 있는 장점은 다음과 같다.
1] 문자를 하나씩 전달하는 것이 아닌 묶어서 한 번에 전달하므로, 전송 시간이 적게 걸려 성능이 향상된다.
2] 사용자가 문자를 잘못 입력했을 경우 수정을 할 수가 있다.
- 하지만 입력 작업에 버퍼를 사용하는 것이 반드시 좋은 것만은 아니다.
- 빠른 반응이 요구되는 게임과 같은 프로그램에서는 키를 누르는 즉시 바로 전달되어야만 한다.
- 이렇게 버퍼를 사용하는 입력과 버퍼를 사용하지 않는 입력은 서로 다른 용도로 사용된다.
- 따라서 자신의 목적에 맞게 버퍼의 사용 여부를 판단해야 한다.
- 참고로 대부분의 C++ 프로그램은 입력 시에는 사용자가
Enter
키를 누르면 입력 버퍼를 비우고, 출력 시에는'\n'
(개행 문자)를 전달 받으면 출력 버퍼를 비우게 된다.
3. 파일 입출력
1) 파일이란?
- 파일이란 의미있는 정보를 담고 있으며, 이름을 가지고 있는 저장 장치 상의 논리적인 단위를 의미한다.
- C++은 이러한 파일을 바이트별로 따로 읽을 수 있는 연속적인 바이트의 집합으로 취급한다.
2) 파일의 종류
- 컴퓨터는 파일을 다음과 같이 두 가지 종류로 나누어서 다룬다.
1] 바이너리 파일
2] 텍스트 파일
- 바이너리 파일은 데이터의 저장과 처리를 목적으로
0
과1
의 이진 형식으로 인코딩된 파일을 가리킨다. - 프로그램이 이 파일의 데이터를 읽거나 쓸 때는 데이터의 어떠한 변환도 일어나지 않는다.
- 텍스트 파일은 사람이 알아볼 수 있는 문자열로 이루어진 파일을 가리킨다.
- 프로그램이 이 파일의 데이터를 읽거나 쓸 때는 포맷 형식에 따라 데이터의 변환이 일어난다.
3) C++ 파일 입출력 클래스
- C++에서 파일의 입출력을 표준 입출력처럼 처리한다.
- C++ 표준 입출력은
iostream
헤더 파일의istream
클래스와ostream
클래스의 멤버 함수로 처리한다. - 이와 마찬가지로 C++ 파일 입출력은
fstream
헤더 파일의 클래스 라이브러리를 가지고 처리하게 된다. - 파일의 입력은
ifstream
클래스로, 파일의 출력은ofstream
클래스의 멤버 함수로 처리한다. - 이 클래스는
iostream
헤더 파일의 클래스로부터 파생된 클래스이다.
4) 파일의 입출력
- C++에서 파일에 대한 입출력 동작은 다음과 같은 순서에 따라 진행된다.
1] 스트림을 관리하기 위한 ifstream
(또는 ofstream
) 객체 생성
2] 특정 파일과의 연결
3] cin
(또는 cout
) 객체와 같은 방법으로 객체를 사용하여 입출력 수행
4] 모든 작업이 끝나면 파일과의 연결 종료
- 특정 파일과의 연결은
open()
멤버 함수를 사용하여 다음과 같이 수행한다.
- 위에서 살펴본
ifstream
객체의 생성과 파일과의 연결을 다음과 같이 한 줄로 결합할 수도 있다.
- 이렇게 연결된 파일에서
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()
멤버 함수를 통해 명시적으로 파일과의 연결을 닫을 수도 있다.
5) 스트림의 상태 검사
- C++의 파일 입출력 클래스는 스트림의 상태를 검사해 주는
ios_base
클래스의 멤버 함수를 상속 받는다.
- 과거의 C++에서는 파일을 성공적으로 열었는지를 다음과 같은 방법으로 확인했다.
1. if (ifs.fail()) { ... } // 파일을 여는데 실패한 경우
2. if (!ifs.good()) { ... } // 파일을 여는데 실패한 경우
3. if (!ifs) { ... } // 파일을 여는데 실패한 경우
- 최신의 C++에서는 파일의 성공적인 개방을
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()
멤버 함수를 통해 스트림에 파일을 연결할 때 파일 모드를 지정하기 위한 두 번째 매개변수로 사용된다.
- 기본적으로
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+"
- 파일을 읽고 쓰는 것이 둘 다 가능한 모드로 개방함
- 파일이 없으면 새 파일을 만들고, 파일이 있으면 해당 파일의 모든 데이터를 지우고 개방함