[CPP] 파일 입출력

Copyright (c) 2016-, All Rights Reserved by Kwanghoon Choi
(아래 CPP 프로그래밍 강의 교재의 내용을 자유롭게 이용하되 다른 웹 사이트 등을 통한 배포를 금합니다.)

C++ 입출력 프로그래밍

1. 파일 입출력 방법

C++ 프로그램에서 표준 입출력(standard input/output, 키보드로 부터 입력 받고 화면으로 출력)하기 위해서 istream 클래스의 객체cin과  ostream 클래스의 cout 객체를 사용한다.

파일로 부터 입력 받고 파일로 출력하기 위해서는 ifstream 클래스와 ofstream 클래스를 사용한다. 파일 입출력 클래스를 사용하기 위해 fstream 헤더 파일을 포함시킨다. 이때 입출력 방식은 표준 입출력 방식과 매우 유사하다. 다만, cin과 cout 객체와 같이 미리 준비되어 있지 않고, 파일을 지정해서 ifstream/ofstream 클래스의 객체를 직접 만든다.

#include <fstream>
using namespace std;

    ...

ifstream inStream;   // 파일 입력 객체 선언
ofstream outStream;  // 파일 출력 객체 선언

inStream.open("infile.txt");    // 입력 파일 열기
outStream.open("outfile.txt");  // 출력 파일 열기

int first, second, thrid;
inStream >> first >> second;             // cf. cin >> first >> second
outStream << "first=" << first << endl;  // cf. cout << "first=" << first << endl;
outStream << "second=" << second << endl;

inStream.close();  // 입력 파일 닫기
outStream.close(); // 출력 파일 닫기

파일 infile.txt에 숫자 123과 456이 기록되어 있다고 가정하면, 파일 outfile.txt에 first=123과 second=456이 기록될 것이다.

파일에 기록된 내용을 모두 (파일의 끝에 도달할 때까지) 읽는 방법은 cin 객체로 입력의 끝(EOF, Ctrl+Z)에 도달할 때까지 반복해서 입력을 받아들이는 방법과 유사하다. 예를 들어, 파일에 정수들이 기록되어 있다고 가정할 때 그 숫자를 모두 읽어 화면에 출력하는 코드는 다음과 같이 작성한다.

(1) 
... 파일 열기 ...

int next;
while ( inStream >> next ) {   // cf. while ( cin >> next ) 
    cout << next << endl; } 

... 파일 닫기 ... 

(2)  
... 파일 열기 ... 
int next; 
while (inStream.eof() == false ) {     
    inStream >> next;
    cout << next << endl;
}

... 파일 닫기 ...

(2)에서 ifstream 클래스의 eof() 멤버 함수를 통해 파일 읽기 도중 파일 끝에 도달 했는지 여부를 확인할 수 있다.

Q1. 아래의 내용을 담고 있는 파일 c.txt를 읽어 C를 C++로 모두 교체해서 cpp.txt에 기록하는 프로그램을 작성하시오.

(힌트: char형 변수 ch에 키보드에서 한 문자를 입력 받는 방법 cin.get( ch )와 유사하게 ifstream 클래스 객체 inStream를 이용해서 inStream.get( ch )와 같이 작성하여 지정한 파일로 부터 한 문자를 입력 받는다.)

C is one of the world's most modern programming languages. There is no language as versatile as C, and C is fun to use.

Q2. 문자열로 부터 입력 받고, 문자열에 출력할 때 사용하는 스트림 클래스도 ifstream이나 ofstream을 사용하는 방법과 유사하게 사용할 수 있다. 이 두 가지 문자열 스트림 클래스 이름과 이 클래스들을 사용하기 위해 포함시키는 헤더 파일 이름은? (힌트: 문자열에 관한 강의 노트 참고)

내용을 이미 담고 있는 파일을 열어 새로운 내용을 추가하려면 다음과 같이 ofstream.open() 멤버 함수의 인자에 ios::app를 지정한다. (cf. app: append)

ofstream fout;
fout.open("data.txt", ios::app);

ios는 입출력 관련된 상수를 모아놓은 표준 라이브러리 클래스이다. ios::app는 이 클래스에 선언된 상수이다.

Q3. 아래 C++ 참고 사이트에서 상수 ios::app에 관한 설명을 담고 있는 웹 페이지를 찾아 그 설명을 옮겨 적으시오.
- https://msdn.microsoft.com/ko-kr/library/3bstk3k5.aspx

ifstream/ofstream 클래스와 함께 fstream 클래스를 이용해서도 파일 입출력을 수행할 수 있다. 차이점은, ifstream/ofstream 클래스는 파일의 처음 부터 끝까지 순서대로 읽거나 출력하는 방식이지만 fstream 클래스를 통해 임의의 순서로 읽거나 출력할 수 있다는 것이다.

  • 순차 읽기/쓰기 : ifstream/ofstream
  • 임의 읽기/쓰기 : fstream
#include <fstream>
using namespace std;

fstream rwStream;

rwStream.open ( “stuff”, ios::in | ios::out );  // 입출력 모드를 지정

rwStream.seekp ( 1000 );  // 1000번째 바이트 위치로 put-pointer를 이동
rwStream.seekg ( 1000 );  // 1000번째 바이트 위치로 get-pointer를 이동

rwStream.get ( symbol );  // char symbol;
rwStream.put ( symbol );

rwStream.close ();

fstream.seekp() 멤버 함수는 파일에 기록할 위치를 조정하고, fstream.seekg() 멤버 함수는 파일을 읽을 위치를 조정한다. 위의 예제는 stuff이라는 이름의 (바이너리) 파일을 열고, 앞에서 부터 1000번째 바이트를 읽고 그대로 다시 기록한다.

텍스트 파일을 읽거나 쓸 때는 ifstream/ofstream 클래스를 이용해서 순차적으로 다루는 것이 적합하고, 바이너리 파일을 읽거나 쓸 때는 fstream 클래스를 이용해서 원하는 위치의 바이트를 읽고 쓰는 것이 바람직하다. 예를 들어, MP3 파일과 같은 바이너리 파일에서 이 곡을 부른 가수에 대한 정보를 가져오려면 MP3 형식에 정의한 위치에서 해당 정보만 읽으면 되는데, 이때 fstream 클래스의 임의 읽기 방식이 적합하다.

2. 파일 입출력 도구(Manipulators)

출력할 때 적절한 형식을 설정할 때 ofstream 클래스의 precision(), setf(), unsetf(), width() 멤버 함수를 이용할 수 있다. 예를 들어 double 형 실수를 출력할 때 소수점 이하 두 자리까지 출력하려면 아래와 같이 작성한다.

cout.setf( ios::fixed );  // 고정 소수점 표기법
cout.setf( ios::showpoint);  // 소숫점과 그 이하 0을 표시
cout.precision( 2 );  // 소수점 이하 두 자리
cout << 3.141592 << endl;

ofstream outStream("output.txt");   

outStream.setf( ios::fixed );  // 고정 소수점 표기법
outStream.setf( ios::showpoint);  // 소숫점과 그 이하 0을 표시
outStream.precision( 2 );  // 소수점 이하 두 자리
outStream << 3.141592 << endl;

setf() 멤버함수를 통해 형식을 지정하고, unsetf() 함수를 통해 해제한다. width() 멤버함수로 출력할 자리의 폭을 확보한다.

(1)
cout.setf( ios::fixed | ios::showpoint | ios::right );  // |는 비트 연산자 OR로 3가지 옵션을 모두 설정


(2)
cout << "Start Now";
cout.width(4);
cout << 7 << endl;

Start Now를 출력하고 3개의 공백을 출력한 다음 7을 출력하는데, cout.width(4)가 없으면 Start Now7과 같이 붙여서 출력한다.

더 많은 출력 옵션을 지정하는 방법은 교재 디스플레이 12.5와 12.6을 참고한다.

출력 옵션을 cout.setf()나 outStream.setf() 등을 통해 지정하는 방법 이외에 조작자(manipulator)를 통해서 설정할 수 있다. 조작자로 endl, setw, setprecison, flush가 있다. iomanip 헤더 파일을 포함 시켜 사용할 수 있다.

cout << "Start " << setw(4) << 10
<< setw(4) << 20 << setw(6) << 30;

setw(4)는 다음에 출력될 내용을 위해 4자리를 확보한다. 위 코드를 실행하면, t와 10 사이에 공백 2개, 10과 20 사이에 공백 2개, 20과 30 사이에 공백 4개를 출력한다.

cout.setf( ios::fixed );
cout.setf( ios::showpoint );
cout << "$" << setprecision(2) << 10.3 << endl
     << "$" << 20.5 << endl;

// 출력 결과
$10.30
$20.50

Q4. 다음 코드의 출력 결과를 작성하시오.

cout << "*" << setw(6) << 1234 << "*"
     << 1234 << "*" << endl;
cout.setf( ios::showpos );
cout << "*" << setw(6) << 1234 << "*"
     << 1234 << "*" << endl;
cout.unsetf( ios::showpos );
cout.setf( ios::left );
cout << "*" << setw(6) << 1234 << "*"
     << setw(6) << 1234 << "*" << endl;

3. 입출력 클래스 상속 관계를 이용

그동안 소개한 입출력 관련 클래스들은 아래의 상속 관계를 가지고 있다.

ioclasses

입출력 프로그램을 작성할 때 위 클래스들의 상속 관계를 고려해서 가장 일반적인 클래스를 선택해서 작성하는 것이 바람직하다.

(1) 일반적인 입력
void sum (istream& source) {
   int n1, n2;
   source >> n1 >> n2;
   cout << n1 << " + " << n2 << " = " << (n1+n2) << endl;
}

sum ( cin );  // 표준 입력

ifstream file("input.txt");  // 파일 입력
sum ( file );

istringstream str ( "123 456" );  // 문자열 입력
sum ( strstream );


(2) 일반적인 출력
void sayHello(ostream& target) {
   target << "Hello";
}

sayHello( cout ); // 표준 출력

ofstream file("output.txt");  // 파일 출력
sayHello( file );

ostringstream str;  // 문자열 출력
sayHello ( str );

(1)에서 키보드로 123 456을 입력하거나, 파일 input.txt에 123 456을 기록해서 입력하거나, 문자열 "123 456"을 통해 123 456을 입력해서 그 덧셈 결과를 화면에 출력한다. (2)에서 "Hello"는 화면에 출력되거나, 파일 output.txt에 출력하거나 문자열 (스트림) str에 출력한다.

 

Q. 행맨 프로그램: words.txt에 기록된 단어들 중 하나를 보여주고 맞추는 게임. 처음에 단어에 포함된 문자 중 알파벳인 것은 -로 표시하고, 알파벳이 아닌 문자는 보여준다. 사용자가 알파벳을 입력하면 단어 안에 일치하는 문자를 모두 보여준다. 입력한 문자가 단어에 없으면 틀린 횟수를 1증가시킨다. 단어의 모든 문자가 공개되거나, 틀린 횟수가 5회가 되면 게임을 종료한다.

Leave a Reply

Your email address will not be published. Required fields are marked *