12장 스트림 및 파일 입출력

2023. 11. 27. 17:17프로그래밍 공부/OOP

12.1 입출력 스트림

스트림(stream): 문자(또는 다른 종류의 데이터)의 흐름 

입력 스트림(input stream): 흐름이 프로그램으로 흘러 들어가는 경우

출력 스트림(output stream): 흐름이 프로그램 밖으로 나간 경우

입력 스트림이 키보드에서 흐르면 프로그램은 키보드에서 입력을 받을 것이다.

입력 스트림이 파일에서 흐르면 프로그램은 해당 파일에서 입력을 받을 것이다.

마찬가지로 출력 스트림은 화면으로 이동하거나 파일로 이동할 수 있다.


인지하지 못할 수도 있지만 이미 프로그램에서 스트림을 사용하고 있다. 

cin: 키보드에 연결된 입력 스트림

cout: 화면에 연결된 출력 스트림

이 두 스트림은

헤더 파일 이름을 <iostream>으로 지정하는 include 지시문와 

std 네임스페이스에 대한 using 지시문이 모두 있는 한

자동으로 프로그램에서 사용할 수 있다.


파일에서 나오거나 파일로 이동하는 다른 스트림을 정의할 수 있다. 

정의한 후에는 cin 및 cout 스트림을 사용하는 것과 같은 방식으로 프로그램에서 스트림을 사용할 수 있다. 

예를 들어 프로그램에서 일부 파일에서 나오는 inStream이라는 스트림을 정의했다고 가정해 보겠다. 

정의하는 방법은 다음과 같다. 

그런 다음 프로그램에서 다음을 사용하여 Number라는 int 변수를 이 파일의 숫자로 채울 수 있다:
int theNumber;
inStream >> theNumber;
마찬가지로 프로그램에서 다른 파일로 이동하는 outStream이라는 이름의 출력 스트림을 정의하면 

변수 the Number의 값을 이 다른 파일로 출력할 수 있다.
다음은 "Number is" 문자열을 출력한 다음 

변수 the Number의 내용을 outStream에 연결된 출력 파일에 출력한다:
outStream << "theNumber is " << theNumber << endl;
스트림이 원하는 파일에 연결되면 프로그램은 

키보드와 화면을 사용하여 I/O와 동일한 방식으로 파일 I/O를 수행할 수 있다.

 

파일 입출력

이 장에서 I/O에 사용할 파일은 텍스트 파일이다.

즉, C++ 프로그램이 포함된 파일과 동일한 종류의 파일이다.

 

읽기와 쓰기
파일에서 읽기(reading): 프로그램이 파일에서 입력을 받는 것

파일에서 쓰기(writing): 프로그램이 출력을 파일로 보내는 것

파일에서 입력을 읽는 다른 방법도 있지만

이 하위 섹션에 제공된 방법은 파일을 처음부터 끝까지 읽는다

(또는 프로그램이 종료하기 전에 읽는 방법).

이 방법을 사용하면 프로그램이 파일의 어떤 것도 두 번째로 백업하여 읽는 것이 허용되지 않는다.

프로그램이 키보드에서 입력을 받을 때

바로 이러한 현상이 발생하므로 새롭거나 이상하게 보여서는 안 된다.

 

마찬가지로 여기에 제시된 방법의 경우

프로그램이 출력을 파일의 처음부터 파일에 쓰고 앞으로 진행한다.

프로그램이 이전에 작성한 출력을 백업하거나 변경할 수 없다.

프로그램이 출력을 화면에 전송할 때 발생하는 현상이 바로 이와 같다.

더 많은 출력을 화면에 전송할 수는 있지만 백업하거나 화면 출력을 변경할 수는 없다.

파일에서 프로그램에 입력을 받거나 프로그램에서 출력을 파일로 전송하는 방법은

스트림을 통해 프로그램을 파일에 연결하는 것이다.

 

<fstream>
출력을 파일로 보내려면, 프로그램은 먼저 파일을 클래스 ofstream의 (스트림) 개체에 연결해야 한다.

파일로부터 입력을 읽으려면, 프로그램은 먼저 파일을 클래스 ifstream의 (스트림) 개체에 연결해야 한다.

클래스 ifstream과 ofstream은 <fstream> 라이브러리에 정의되어 있고 std 네임스페이스에 있다.

따라서, 파일 입력과 파일 출력을 모두 수행하려면, 프로그램은 다음을 포함해야 한다
#include <fstream>
using namespace std;
or
#include <fstream>
using std::ifstream;
using std::ofstream;

 

스트림 선언하기
다른 클래스 변수를 선언할 때와 마찬가지로 스트림을 선언해야 한다. 

inStream: 파일의 입력 스트림

outStream: 다른 파일의 출력 스트림
ifstream inStream;
ofstream outStream;
이전에 선언된 inStream이나 outStream과 같은 스트림 변수들은 각각 파일에 연결되어야 한다.

open 멤버 함수: 파일을 여는 것(opening the file)

예를 들어, 스트림의 입력 스트림이 infile.txt라는 이름의 파일에 연결되기를 원한다고 가정해 보겠다.

그런 다음 프로그램에 다음과 같은 내용이 포함되어야 한다
이 파일에서 입력을 읽기 전에 다음을 수행한다:
inStream.open("infile.txt");

 

경로 이름
파일 이름을 지정할 때 경로 이름(pathname)(디렉토리 또는 폴더)을 지정할 수 있다.

경로 이름을 지정하는 방법에 대한 자세한 내용은 시스템마다 조금씩 다르다.

예제에서는 단순한 파일 이름을 사용할 것인데,

파일이 프로그램이 실행 중인 디렉터리와 동일한 디렉터리(폴더)에 있다고 가정한다.

 

open 함수를 사용하여 입력 스트림 변수를 선언하고

파일에 연결한 후 프로그램은 추출 연산자 >>를 사용하여

파일에서 입력을 가져올 수 있으며 입력 스트림 변수는 cin과 동일한 방식으로 사용된다.

예시)

다음은 inStream에 연결된 파일에서 두 개의 입력 번호를 읽고

oneNumber와 otherNumber 변수에 배치한다:
int oneNumber, anotherNumber;
inStream >> oneNumber >> anotherNumber;

 

출력 스트림은 입력 스트림에 대해 설명한 것과 같은 방식으로 열린다(즉, 파일에 연결됨). 

예시)

다음은 출력 스트림을 outStream으로 선언하고 outfile.txt라는 이름의 파일에 연결한다:
ofstream outStream;
outStream.open("outfile.txt");

 

스트림 유형의 스트림과 함께 사용할 경우,

멤버 함수 open은 출력 파일이 이미 존재하지 않는 경우, 출력 파일을 생성한다.

출력 파일이 이미 존재하는 경우,

멤버 함수 open은 파일 내용을 폐기하여, 열려는 호출 후 출력 파일이 비어 있도록 한다.

 

파일이 열려는 호출과 함께 스트림 outStream에 연결된 후

프로그램은 삽입 연산자 <<를 사용하여 해당 파일에 출력을 보낼 수 있다. 

예시)

다음은 두 개의 문자열과 변수 oneNumber 및 oneNumber의 내용을 

스트림 outStream에 연결된 파일(이 예에서는 outfile.txt로 명명된 파일)에 기록한다:
outStream << "oneNumber = " << oneNumber
                   << " anotherNumber = " << anotherNumber;

 

>> 및 <<의 오버로딩을 파일에 적용하다
8장에서 지적한 바와 같이 >>와 <<을 오버로딩하면

이 오버로딩이 파일 입력 및 출력 스트림에도 cin과 cout에 적용되는 것과 동일하게 오버로딩이 적용된다. 

 

외부 파일 이름

프로그램에서 파일을 다룰 때는 파일 이름이 두 개인 것과 같다.

외부 파일 이름(external file name): 운영체제에서 사용하는 일반적인 파일 이름

운영 체제에서 사용하는 규칙에 따라 파일 이름을 지정해야 한다.

외부 파일 이름은 파일의 실제 이름이지만 일반적으로 프로그램에서 한 번만 사용된다.

외부 파일 이름은 함수 열기에 대한 인수로 주어지지만

파일이 열린 후에는 항상 파일에 연결된 스트림의 이름을 지정하여 파일을 참조한다.

따라서 프로그램 내에서 스트림 이름은 파일의 두 번째 이름으로 사용된다.

 

파일 이름이 두 개인 경우
프로그램에서 사용하는 모든 입력 파일과 출력 파일에는 모두 두 개의 이름이 있다.

외부 파일 이름은 파일의 실제 이름이지만, 파일을 스트림에 연결하는 멤버 함수 열기 호출에서만 사용된다.

열기 호출 후에는 항상 스트림 이름을 파일 이름으로 사용한다.

 

close

디스플레이 12.1의 샘플 프로그램은

하나의 파일에서 세 개의 숫자를 읽고 그 숫자의 합계와 함께 일부 텍스트를 다른 파일에 쓴다.

프로그램이 파일에서 입력을 받거나 파일에 출력을 보내는 것이 끝나면 모든 파일은 닫아야 한다(close).

파일을 닫으면 파일에서 스트림의 연결이 끊어진다.

함수 close에 대한 호출되면서 파일이 닫힌다.

디스플레이 12.1의 프로그램에서 다음 행은 함수 close를 사용하는 방법을 보여준다:
inStream.close( );
outStream.close( );
함수 close는 인수가 없다는 것을 주목해라.

프로그램이 정상적으로 종료되지만 파일을 닫지 않으면 시스템이 자동으로 파일을 닫는다. 

하지만 적어도 두 가지 이유로 파일을 닫는 습관을 들이는 것이 좋다. 

1. 프로그램이 정상적으로 종료되는 경우에만 파일이 닫힌다.

오류로 인해 프로그램이 비정상적으로 종료되는 경우 

파일이 닫히지 않고 손상된 상태로 남아 있을 수 있다.

프로그램이 파일을 종료하는 즉시 파일을 닫으면 파일 손상이 발생할 가능성이 낮다.

2. 프로그램이 파일에 출력을 보내고 나중에 해당 출력을 다시 프로그램에 읽도록 할 수 있다.

이를 수행하려면 프로그램이 파일에 쓰기를 마친 후 파일을 닫고 입력 스트림으로 파일을 다시 열어야 한다.
(입력과 출력 모두 파일을 여는 것은 가능하지만 이는 조금 다른 방식으로 진행된다.)

 

flush
덜 일반적으로 사용되는 멤버 함수는 flush인데, 이 함수는 모든 출력 스트림의 멤버 함수이다.

효율성 때문에 출력이 실제로 파일에 쓰이기 전에 버퍼링(buffering)

즉, 어딘가에 일시적으로 저장되는 경우가 많다.

멤버 함수 flush는

출력 스트림을 플러시하여

버퍼링되었을 수 있는 모든 출력이 파일에 물리적으로 기록되도록 한다.

닫기를 호출하면

자동으로 flush가 호출되므로 flush를 사용할 필요가 거의 없다.

flush의 구문은 다음 예제로 표시된다:
outStream.flush( );

 

디스플레이 12.1 단순한 파일 입출력

//Reads three numbers from the file infile.txt, sums the numbers,
//and writes the sum to the file outfile.txt.
//A better version of this program is given in Display 12.3.
#include <fstream>
using std::ifstream;
using std::ofstream;
using std::endl;

int main( )
{
	ifstream inStream;
	ofstream outStream;
    
	inStream.open("infile.txt");
	outStream.open("outfile.txt");
    
	int first, second, third;
	inStream >> first >> second >> third;
	outStream << "The sum of the first 3\n"
	          << "numbers in infile.txt\n"
	          << "is " << (first + second + third)
	          << endl;
              
	inStream.close( );
	outStream.close( );
    
	return 0;
}

 

샘플 대화 상자

화면에 출력이 없고 키보드의 입력도 없다.

 

Infile.txt

(프로그램에 의해 변경되지 않음)

1
2
3
4

 

Outfile.txt

(프로그램 실행 후)

The sum of the first 3
numbers in infile.txt
is 6


함정: 스트림 변수에 대한 제한사항

일반적인 방법으로

스트림 변수(ifstream 또는 ofstream 유형 중 하나)를 선언하지만, 

이러한 변수는 다른 변수가 사용되는 일부 방식에서는 사용할 수 없다.

할당문을 사용하여 스트림 변수에 값을 할당할 수 없다.

스트림 유형의 매개 변수(ifstream, ofstream 또는 다른 스트림 유형)를 가질 수 있지만

call-by-reference 매개 변수여야 한다.

call-by-value 매개 변수는 될 수 없다.

 

파일에 추가하기

출력물을 파일로 보낼 때

코드는 먼저 멤버 함수 open을 사용하여 파일을 열고 파일을 스트림 유형에 연결해야 한다.

파일 이름을 인수 하나로 지정하면 항상 빈 파일이 생성된다.

지정한 이름의 파일이 이미 있으면 이전 파일의 내용이 손실된다.

파일을 여는 다른 방법이 있으므로

프로그램의 출력물이 이미 파일에 있는 데이터 뒤에 파일에 추가된다.

예시)
출력을 "important.txt"라는 이름의 파일에 추가하려면

다음과 같이 두 인수 버전의 open을 사용한다:
ofstream outStream;
outStream.open("important.txt", ios::app);
"important.txt" 파일이 존재하지 않으면 

해당 이름의 빈 파일이 생성되어 프로그램의 출력을 받게 되며, 

파일이 이미 존재하는 경우에는

해당 파일의 모든 출력이 파일 끝에 추가되어 파일의 오래된 데이터가 손실되지 않도록 한다.

 

ios::app
두 번째 인수 ios::app: 클래스 ios에서 정의된 상수이다.

클래스 ios는 <iosstream> 라이브러리(및 일부 다른 스트림 라이브러리)에서 정의된다.

클래스 ios의 정의는 std 네임스페이스에 있으므로

다음 중 하나를 사용하면 ios(따라서 ios:app)를 프로그램에서 사용할 수 있다:

#include <iostream>
using namespace std;
or
#include <iostream>
using std::ios;

 

 

디스플레이 12.2 파일에 추가

//Appends data to the end of the file alldata.txt.
#include <fstream>
#include <iostream>
using std::ofstream;
using std::cout;
using std::ios;

int main( )
{
	cout << "Opening data.txt for appending.\n";
	ofstream fout;
    
	fout.open("data.txt", ios::app);
	fout << "5 6 pick up sticks.\n"
	     << "7 8 ain't C++ great!\n";
         
	fout.close( );    
	cout << "End of appending to file.\n";
    
	return 0;
}

 

샘플 대화 상자

Data.txt

(프로그램 실행 전)

1 2 buckle my shoe.
3 4 shut the door.

 

Data.txt

(프로그램 실행 후)

1 2 buckle my shoe.
3 4 shut the door.
5 6 pick up sticks.
7 8 ain't C++ great!

 

Screen Output

Opening data.txt for appending.
End of appending to file

 

파일에 추가
파일에 데이터를 추가하여 파일의 기존 내용을 수행하려면 다음과 같이 파일을 연다.
SYNTAX
Output_Stream.open(File_Name, ios::app);
EXAMPLE
ofstream outStream;
outStream.open("important.txt", ios::app);

 

팁: 파일을 열기 위한 또 다른 구문

ifstream과 ofstream 클래스는

각각 파일 이름과 파일을 열기 위한 다른 매개변수를 지정할 수 있는 생성자를 가지고 있다.

몇 가지 예는 구문을 명확하게 할 것이다.
그 두가지 구문은
ifstream inStream;
inStream.open("infile.txt");
다음의 동등한 문장으로 대체할 수 있다:
ifstream inStream("infile.txt");
그 두가지 구문은
ofstream outStream;
outStream.open("outfile.txt");
다음의 동등한 문장으로 대체할 수 있다:
ofstream outStream("outfile.txt");
우리의 마지막 예로, 두 가지 구문은
ofstream outStream;
outStream.open("important.txt", ios::app);
는 다음과 동등하다:
ofstream outStream( "important.txt", ios::app);

 

디스플레이 12.3 

//Reads three numbers from the file infile.txt and writes the sum to the
//file outfile.txt.
#include <fstream>
#include <iostream>
#include <cstdlib> //for exit
using std::ifstream;
using std::ofstream;
using std::cout;
using std::endl;

int main( )
{
	ifstream inStream;
	ofstream outStream;
    
	inStream.open("infile.txt");
	if (inStream.fail( ))
	{
		cout << "Input file opening failed.\n";
		exit(1);
	}
    
	outStream.open("outfile.txt");
	if (outStream.fail( ))
	{
		cout << "Output file opening failed.\n";
		exit(1);
	}
    
	int first, second, third;
	inStream >> first >> second >> third;
	outStream << "The sum of the first 3\n"
	          << "numbers in infile.txt\n"
	          << "is " << (first + second + third) << endl;
              
	inStream.close( );
	outStream.close( );
	return 0;
}

 

샘플 대화 상자

(file.txt 파일이 없는 경우)

Input file opening failed.

 

 

팁: 파일이 성공적으로 열렸는지 확인하기

열려고 하는 호출은 여러 가지 이유로 인해 실패할 수 있다.

예를 들어, 입력 파일을 열려고 하는데

지정한 외부 이름을 가진 파일이 없으면 열려고 하는 호출은 실패한다.

다른 예를 들어, 파일이 존재하고 프로그램(즉, 계정)에 파일에 대한 쓰기 권한이 없기 때문에

출력 파일을 열려는 시도가 실패할 수 있다.

이러한 일이 발생하면 오류 메시지가 표시되지 않을 수 있고

프로그램이 단순히 예기치 않은 일을 수행하도록 진행될 수 있다.

따라서 열려고 하는 호출이 성공했는지 여부를 확인하기 위해 항상 호출을 수행하고,

열려고 하는 호출이 실패했다면 프로그램을 종료(또는 다른 적절한 조치)해야 한다.

 

멤버 함수 fail
멤버 함수 fail: 스트림 작업이 실패했는지 여부를 테스트할 수 있다.

ifstream과 ofstream 클래스 각각에 대해 fail이라는 이름의 멤버 함수가 있다.

fail 함수는 인수를 사용하지 않고 bool 값을 반환한다.
열려는 각 호출이 끝나면 즉시 실패하도록 호출을 해야 한다.

만약 열려는 호출이 실패하면 함수가 true로 반환된다.

예시)

열려는 다음 호출이 실패하면 프로그램은 오류 메시지를 출력하고 종료된다.

호출이 성공하면 실패 함수가 false로 반환되고 프로그램은 계속된다.
inStream.open("stuff.txt");
if (inStream.fail( ))
{
    cout << "Input file opening failed.\n";
    exit(1);
}


디스플레이 12.3은 입력 및 출력 파일이 성공적으로 열렸는지 테스트하기 위해 

다시 작성된 디스플레이 12.1의 프로그램을 포함한다.

이는 디스플레이 12.1의 프로그램과 정확히 동일한 방식으로 파일을 처리한다.

특히, 디스플레이 12.1에 표시된 내용이 infile.txt 파일이 존재하고 있다고 가정하면

디스플레이 12.3의 프로그램은 디스플레이 12.1에 표시된 파일 outfile.txt를 생성한다.
그러나 문제가 발생하여 열기 위한 호출 중 하나가 실패한 경우 

디스플레이 12.3의 프로그램이 종료되고 적절한 오류 메시지가 화면에 전송된다.

예시)

infile.txt라는 이름의 파일이 없으면

inStream.open에 대한 호출이 실패하고 프로그램이 종료되며 오류 메시지가 화면에 기록된다.

오류 메시지를 출력하기 위해 cout을 사용했음을 알 수 있다.

이 프로그램은 cout을 사용하여 화면에 출력하기 때문에

(파일 I/O뿐만 아니라 파일 I/O도 수행)

헤더 파일 <iostream>에 대한 포함 명령을 추가했다.

 

파일 I/O 문 요약
이 예제에서 입력은 file.txt라는 이름을 가진 파일에서 오고

출력은 outfile.txt라는 이름을 가진 파일로 간다.
■ 프로그램 파일에 다음 지침을 포함한다:
#include <fstream>
#include <iostream>
#include <cstdlib>
지시문(또는 이와 유사한 것)을 사용하여 다음을 추가한다:
using std::ifstream;
using std::ofstream;
using std::cout;
using std::endl; //if endl is used.
■ 입력 스트림의 스트림 이름을 선택하고 ifstream 유형의 변수임을 선언한다.

출력 파일의 스트림 이름을 선택하고 스트림의 유형임을 선언한다.

예를 들어,
ifstream inStream;
ofstream outStream;
■ 외부 파일 이름을 인수로 하여 열린 멤버 함수를 사용하여 각 스트림을 파일에 연결한다.

멤버 함수를 사용하여 열려는 호출이 성공적인지 테스트하지 못했음을 기억하라:
inStream.open("infile.txt");
if (inStream.fail( ))
{
    cout << "Input file opening failed.\n";
    exit(1);
}
outStream.open("outfile.txt");
if (outStream.fail( ))
{
    cout << "Output file opening failed.\n";
    exit(1);
}
■ 당신이 cin을 사용하여 키보드에서 입력을 받는 것처럼

Stream의 instream을 사용하여 file.txt의 파일에서 입력을 가져온다.

예를 들어,
inStream >> someVariable >> someOtherVariable;

■ cout을 사용하여 출력을 화면으로 보내는 것처럼 

stream의 outStream을 사용하여 출력을 file.txt 파일로 보낸다.

예를 들어,
outStream << "someVariable = "
<< someVariable << endl;
■ close 함수를 사용하여 스트림을 닫는다:
inStream.close( );
outStream.close( );

 

문자 입출력

9장에서는 cin을 사용한 키보드와 cout을 사용한 화면의 문자 입출력에 대해 설명했다.

파일의 문자 입출력은 키보드와 화면의 문자 입출력과 동일한 방식으로 작동한다.

cin 대신 파일에 연결된 입력 스트림을 사용하거나

cout 대신 파일에 연결된 출력 스트림을 사용하면 된다.

특히 get, getline, putback, peek, ignore는

파일 입력의 경우 키보드 입력의 경우와 동일한 작업을 수행하고,

파일 출력의 경우 화면 출력의 경우와 동일한 작업을 수행한다.

 

파일 마무리 확인하기

입력 파일을 처리하는 일반적인 방법은

파일의 끝에 도달할 때까지 파일의 데이터를 처리하는 반복문을 사용하는 것이다. 

파일의 끝을 검사하는 두 가지 표준 방법이 있다. 

 

eof 멤버 함수

1. 가장 간단한 방법은 eof 멤버 함수를 사용하는 것이다.
모든 입력 파일 스트림에는 eof 멤버 함수가 있다.

eof 멤버 함수: 입력 파일의 끝에 도달하기 위한 테스트에 사용할 수 있다.

eof의 함수는 인수를 사용하지 않으므로

입력 스트림을 fin이라고 하면 eof의 함수에 대한 호출이 fin.eof( )로 기록된다
이것은 bool 식으로, 반복문 do-while문 또는 if-else 문을 제어하는 데 사용할 수 있다. 

프로그램이 입력 파일의 끝을 지나서 읽었을 경우 이 식은 true를 반환하고,

그렇지 않을 경우 false를 반환한다.

 

eof 함수로 입력 반복문 종료

일반적으로 파일의 끝에 없음을 테스트하고 싶으므로, 

e의 멤버 함수에 대한 호출은 일반적으로 앞에 not과 함께 사용된다. 

C++에서 기호!는 not을 표현하기 위해 사용된다는 것을 기억하자. 

예시)

Stream의 입력 스트림에 연결된 파일의 전체 내용을 다음과 같이 화면에 기록하고 루프를 수행할 수 있다:
inStream.get(next);
while (! inStream.eof( ))
{
    cout << next; //If you prefer, you can use cout.put(next) here.
    inStream.get(next);
}
이전의 while문은 멤버 함수 get을 사용하여 

입력 파일에서 각 문자를 char 변수에 읽어들인 후 화면에 문자를 쓴다.
프로그램이 파일의 끝을 지나면 inStream.eof()의 값이 된다
false에서 true로 변경된다.

따라서,
(! inStream.eof( ))
true에서 false로 변경되고 루프가 종료된다.
Stream.eof( )에서는 프로그램이 파일 끝을 넘어 한 문자를 읽어보려 할 때까지 true가 되지 않는다. 

예시)

파일에 다음이 포함되어 있다고 가정한다

(c 뒤에 새 줄 문자가 없음):
AB
c

이것은 실제로 다음 네 명의 캐릭터 목록이다:
ab<the newline character '\n' >c
표시된 루프는 'a'를 읽고 화면에 기록한 다음, 

'b'를 읽고 화면에 기록한 다음, 

새로운 행 문자 '\n'을 읽고 화면에 기록한 다음, 

'c'를 읽고 화면에 기록한다. 

그 시점에서 루프는 파일의 모든 문자를 읽게 된다. 

그러나 Stream.eof( )에서는 여전히 false이다.

inStream.eof( )의 값은 계속해서 false이다
프로그램이 문자를 하나 더 읽어보기 전까지는 false에서 true로 변경되지 않는다.
이전 while문이 inStream.get(다음)으로 끝나는 이유이다. 

루프를 종료하려면 루프가 하나의 추가 문자를 읽어야 한다.

 

파일 끝에 특별한 파일 끝 마킹기(end-the-file marker)가 있다.

이 파일 끝 마킹기를 읽을 때까지 eof의 멤버 함수가 false에서 true로 바뀌지 않는다.

그래서 이전의 while 루프가 파일의 마지막 문자라고 생각하는 것 이상으로 한 문자를 읽을 수 있었다.

그러나 이 파일 끝 마킹기는 일반 문자가 아니므로 일반 문자처럼 조작해서는 안 된다.

이 파일 끝 마킹기를 읽을 수는 있지만 다시는 쓰지 말아야 한다.

파일 끝 마킹기를 쓰면 결과는 예측할 수 없다.

시스템은 자동으로 이 파일 끝 마킹기를 각 파일의 끝에 배치한다.


복잡한 문제를 해결하기 위해 일부 컴파일러에서 eof를 구현하면 

파일 종료 마커를 읽을 필요 없이 true로 반환되므로 

컴파일러의 문서를 확인하거나 짧은 프로그램을 작성하여 컴파일러의 동작을 확인해야 할 수 있다.

 

파일의 끝을 테스트하는 마초적인 방법
2. 파일의 끝을 확인하는 두 번째 방법은 

추출 연산자가 있는 읽기가

실제로 bool 값을 반환한다는 사실을 기록(그리고 사용)하는 것이다. 이 식은
(inStream >> next)
읽기가 성공적이면 true로 반환하고

코드가 파일 끝을 넘어 읽기를 시도하면 false로 반환한다.

예시)

다음은 Stream의 입력 스트림에 연결된 정수 파일의 모든 숫자를 읽고 변수 합에서 숫자의 합을 계산한다:
double next, sum = 0;
while ( inStream >> next )
    sum = sum + next;
cout << "the sum is " << sum << endl;
이전 반복문은 좀 특이하게 보일 수 있는데, 

왜냐하면 inStream >>는 다음에 Stream의 스트림에서 숫자를 읽고 bool 값을 반환하기 때문이다.

추출 연산자 >>를 포함하는 식은 동작과 bool 조건이 동시에 된다.
입력할 다른 숫자가 있으면 숫자가 읽혀지고 Boolean 값 true가 반환되므로 루프 본체가 한 번 더 실행된다.

더 이상 읽을 수 있는 숫자가 없으면 아무것도 입력되지 않고 부울값 false가 반환되므로 루프가 종료된다.

이 예제에서는 다음 입력 변수의 유형이 double이었지만

파일의 끝을 확인하는 이 방법은 int나 char와 같은 다른 데이터 유형에서도 동일하게 작동한다.


많은 C++ 프로그래머들이 문화적인 이유로 파일의 끝 부분을 검사하는 이 두 번째 방법을 선호한다.

C 프로그래밍에서는 일반적으로 사용했다.

구현 내용에 따라 이 두 번째 방법이 조금 더 효율적일 수도 있다.

어쨌든 이 두 번째 방법을 사용하든 사용하지 않든 알아야 다른 프로그래머들의 코드를 이해할 수 있다.

eof 멤버 함수를 사용하는 예는 디스플레이 12.4에 나와 있다.

 

디스플레이 12.4 파일 마무리 확인하기

//Copies story.txt to numstory.txt,
//but adds a number to the beginning of each line.
//Assumes story.txt is not empty.
#include <fstream>
#include <iostream>
#include <cstdlib>

using std::ifstream;
using std::ofstream;
using std::cout;

int main( )
{
	ifstream fin;
	ofstream fout;
    
	fin.open("story.txt");
	if (fin.fail( ))
	{
		cout << "Input file opening failed.\n";
		exit(1);
	}
    
	fout.open("numstory.txt");
	if (fout.fail( ))
	{
		cout << "Output file opening failed.\n";
		exit(1);
	}

	char next;
	int n = 1;
	fin.get(next);
	fout << n << " ";
	while (! fin.eof( ))
	{
		fout << next;
		if (next == '\n')
		{
			n++;
			fout << n << ' ';
		}
		fin.get(next);
	}
    
	fin.close( );
	fout.close( );
    
	return 0;
}

루프는 읽기(fin.get)로 끝난다.
구성원 함수 fin.eof는 프로그램이 파일의 마지막 문자를 읽은 후에 한 문자를 더 읽으려고 할 때까지 true로 반환되지 않는다.

 

샘플 대화 상자

화면에 출력이 없고 키보드의 입력도 없다.

 

Story.txt

(프로그램에 의해 변경되지 않음)

The little green men had
pointed heads and orange
toes with one long curly
hair on each toe.

 

Numstory.txt

(프로그램 실행 후)

1 The little green men had
2 pointed heads and orange
3 toes with one long curly
4 hair on each toe.

 

12.2 스트림 입출력을 위한 툴

 

입력으로서 파일 이름

지금까지 입력 파일과 출력 파일의 문자 그대로의 파일 이름을 프로그램의 코드에 기록했다.

다음 예제와 같이 파일 이름을 함수 열기 호출 인수로 지정하여 이 작업을 수행했다:
inStream.open("infile.txt");
대신 다음과 같이 키보드에서 파일 이름을 읽을 수 있다:
char fileName[16];
ifstream inStream;
cout << "Enter file name (maximum of 15 characters):\n";
cin >> fileName;
inStream.open(fileName);

 

C-string으로서
코드는 파일 이름을 C-string으로 읽는다는 것을 유의하자.

멤버 함수 open은 C-string인 인수를 사용한다.

문자열 변수를 여는 인수로 사용할 수 없으며

문자열 개체에서 C-string으로 변환할 미리 정의된 형식의 캐스트 연산자가 없다.

 

string 객체로서

그러나 파일 이름을 문자열 변수로 읽고 문자열 멤버 함수 c_str( )를 사용하여 열 때 대응하는 C-string 값을 생성할 수 있다.

코드는 다음과 같다:
string fileName;
ifstream inStream;
cout << "Enter file name:\n";
getline(cin, filename);
inStream.open(fileName.c_str( ));
파일 이름에 문자열 변수를 사용하는 경우 파일 이름의 크기에는 제한이 없다.

 

스트림 함수로 출력 포맷하기

항목 사이의 간격과 소수점 뒤의 자릿수와 같은 세부 사항을 결정하는 명령을 사용하여 

파일이나 화면으로 출력 형식을 제어할 수 있다.

예시)

1장에서 우리는 돈의 양을 출력하는 데 사용할 "마법의 공식"을 다음과 같이 제시했다:
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
이제 우리는 출력을 포맷하는 이것들과 다른 공식들에 대해 설명할 수 있게 되었다.

첫 번째로 주목할 점은 어떤 출력 스트림에서도 이러한 포맷 명령을 사용할 수 있다는 것이다.

파일에 연결된 출력 스트림은 cout 객체와 같은 멤버 함수를 갖는다.

outStream이 파일 출력 스트림일 경우, 출력을 포맷하는 방법은 다음과 같다:
outStream.setf(ios::fixed);
outStream.setf(ios::showpoint);
Outstream.precision(2);
이 마법 공식을 설명하기 위해 우리는 멤버 함수를 역순으로 고려할 것이다.

 

precision
모든 출력 스트림에는 precision이라는 멤버 함수가 있다.

프로그램에서 outStream 스트림에 대해 이전 것과 같은 precision 호출을 실행하고

그 시점부터 해당 스트림에 출력되는 소수점 이하의 숫자는

컴파일러가 작성된 시점에 따라 총 두 개의 유효숫자 또는 소수점 뒤에 두 자리 숫자로 기록된다.

다음은 두 개의 유효숫자를 설정하는 컴파일러에서 출력할 수 있는 예이다:
23. 23. 2.2e7 2.2 6.9e-1 0.00069
소수점 뒤에 두 자리 수를 설정하는 컴파일러에서 출력할 수 있는 것은 다음과 같다:
23.56 2.26e7 2.21 0.69 0.69e-4
이 책에서 우리는 컴파일러가 소수점 다음에 두 자리 수를 설정한다고 가정한다.

물론 2와는 다른 인수를 사용하여 정확도를 다소 높일 수도 있다.
모든 출력 스트림에는 특정 플래그를 설정하는 데 사용할 수 있는 setf라는 멤버 함수가 있다.

이 플래그들은 std 네임스페이스에 있는 클래스 ios의 상수이다.

 

setf, flag
setf에 대한 호출로 설정하면

플래그가 출력 스트림의 특정 동작을 결정한다.

다음은 스트림 outStream을 호출 개체로 사용하여 멤버 함수 setf에 대한 두 가지 호출이다:
outStream.setf(ios::fixed);
outStream.setf(ios::showpoint);
이 플래그들 각각은 출력을 두 가지 가능한 방법 중 하나로 포맷하라는 명령이다.

스트림이 하게 하는 것은 플래그에 달려 있다.

 

ios::fixed
플래그 ios::fixed: 스트림이 부동 소수점 숫자를 고정 소수점 표기법을 출력한다.

고정 소수점 표기법(fixed-point notation):우리가 일반적으로 숫자를 쓰는 방법에 대한 표현

플래그 ios:fixed가 설정되어 있으면(setf에 대한 호출로),

스트림에 출력되는 모든 부동 소수점 숫자(예를 들어, double형의 숫자)는

전자 표기법이 아닌 일반적인 일상 표기법으로 기록된다.

 

ios::showpoint
플래그 ios::showpoint: 부동 소수점 숫자에 항상 소수점을 포함하도록 스트림에 알려준다.

출력할 숫자의 값이 2.0이면 단순히 2로 출력되지 않고 2.0으로 출력된다.

즉, 소수점 뒤의 숫자가 모두 0이더라도 출력에 소수점이 포함된다.

일부 일반 플래그와 이 플래그가 일으키는 동작은 디스플레이 12.5에 설명되어 있다.

 

setf
setf는 한 번의 호출로 여러 개의 플래그를 설정할 수 있다.

그림과 같이 다양한 플래그를 '|' 기호로 연결하기만 하면 된다.
outStream.setf(ios::fixed | ios::showpoint | ios:right);
출력 스트림에는 precision와 setf 이외에 다른 멤버 함수가 있다.

 

width

매우 일반적으로 사용되는 포매팅 함수 중 하나는 width이다.

예시)

streamcout이 다음과 같은 width 호출을 생각해 보라:
cout << "Start Now";
cout.width(4);
cout << 7 << endl;
이 코드를 사용하면 화면에 다음 줄이 나타난다:
Start Now 7
이 출력에는 문자 'w'와 숫자 7 사이에 정확히 세 개의 공백이 있다.

width 함수: 어떤 항목을 출력으로 줄 때 사용할 공간을 스트림에 알려준다.
이 경우 숫자 7은 한 칸만 차지하고 width는 네 칸을 사용하도록 설정되어 있으므로 세 칸은 비어 있다.

출력에 width 인수에서 지정한 것보다 더 많은 공간이 필요하다면 필요한 만큼 더 많은 공간이 사용된다.

width 인수에 어떤 인수를 부여하든 항목 전체는 항상 출력된다.

 

클래스 ios

클래스 ios에는 ios::app(파일에 추가한다는 것을 표시하는 데 사용됨) 및

Display 12.5에 나열된 플래그와 같은 중요하게 정의된 상수가 많이 있다.

클래스 ios는 <iostream> 및 <fstream>과 같은 출력 스트림의 라이브러리에서 정의된다.

클래스 ios 및 이 모든 상수(이 모든 플래그)를

코드에서 사용할 수 있도록 하는 한 가지 방법은 다음과 같다:

#include <iostream> //or #include <fstream> or both
using std::ios;

 

unsetf

unsetf 함수: 설정된 플래그를 해제하는 함수

예시)

다음과 같이 하면 프로그램이

스트림 cout에 출력되는 양의 정수에 플러스 기호를 포함하지 않게 된다:
cout.unsetf(ios::showpos);
플래그가 설정되면 설정이 해제될 때까지 설정된 상태를 유지한다.

정밀도에 대한 호출의 효과는 정밀도가 재설정될 때까지 유효하다.

그러나 멤버 함수 width는 다른 동작을 한다.

width에 대한 호출은 출력되는 다음 항목에만 적용된다.

각 숫자를 출력하기 위해 4개의 공백을 사용하여 12개의 숫자를 출력하려면

width에 대한 호출을 12번 수행해야 한다.

이것이 성가신 일이 되면 다음 섹션에서 설명하는 조정자 설정을 사용하는 것이 좋다.

 

디스플레이 12.5 setf용 플래그 서식 지정

플래그 플래그를 세팅하는 의미 디폴트
ios::fixed 부동 소수점 번호는 전자 노트에 기록되지 않습니다. (이 플래그를 설정하면 플래그 ios::scientific이 자동으로 해제됩니다.) Not set
ios::scientific 부동 소수점 번호는 e-notation으로 작성됩니다. (이 플래그를 설정하면 플래그 ios::fixed가 자동으로 해제됩니다.) ios::fixed 또는 ios::scientific이 설정되어 있지 않으면 시스템에서 각 번호를 출력하는 방법을 결정합니다. Not set
ios::showpoint 부동 소수점 숫자는 항상 소수점과 후행 0이 표시됩니다. 만약 그것이 설정되지 않으면 소수점 뒤에 모든 0이 있는 숫자는 소수점 뒤에 오는 0이 없이 출력될 수 있습니다. Not set
ios::showpos 플러스 기호는 양의 정수 값 앞에 출력됩니다. Not set
ios::right 이 플래그를 설정하고 일부 필드 너비 값에 멤버 함수 너비 호출이 주어지면 다음 항목 출력은 너비로 지정된 공간의 오른쪽 끝에 있습니다. 즉, 항목 출력 앞에 빈 칸이 추가됩니다. (이 플래그를 설정하면 플래그 ios::left가 자동으로 해제됩니다.) Set
ios::left 이 플래그를 설정하고 일부 필드 너비 값에 멤버 함수 너비 호출이 주어지면 다음 항목 출력은 너비로 지정된 공간의 왼쪽 끝에 있습니다. 즉, 항목 출력 뒤에 빈 칸이 추가됩니다. (이 플래그를 설정하면 플래그 ios::right가 자동으로 해제됩니다.) Not set
ios::dec 정수는 10진수(기준 10) 표기법으로 출력됩니다. Set
ios::oct 정수는 8진수(기준 8) 표기법으로 출력됩니다. Not set
ios::hex 정수는 16진수(기준 16) 표기법으로 출력됩니다. Not set
ios::uppercase 부동 소수점 숫자에 대한 과학적 표기법에서 소문자 e 대신 대문자 E가 사용됩니다. 16진수는 대문자를 사용하여 출력됩니다. Not set
ios::showbase 출력 번호의 기본값을 표시합니다(8진수의 경우 O, 16진수의 경우 Ox 앞). Not set

 

조정자

조정자(manipulator): 비전통적인 방법으로 호출되는 함수

조정자는 마치 조정자 함수 호출이 출력할 항목인 것처럼 삽입 연산자 << 뒤에 배치된다.

전통적인 함수처럼 조정자는 인수를 가질 수도 있고 가지지 않을 수도 있다.

우리는 이미 endl이라는 한 조정자를 보았다.

이 하위 절에서는 setw와 setprecision이라는 두 조정자에 대해 설명한다.

 

setw
조정자 setw와 멤버 함수width (이미 보신)가 정확히 같은 일을 한다.

setw 조정자를 삽입 연산자 <<, 마치 출력 스트림으로 보내려는 것처럼 쓰고,

이것이 멤버 함수 width를 차례로 호출한다.

예시)

다음은 지정된 필드 너비를 사용하여 숫자 10, 20, 30을 출력한다:
cout << "Start" << setw(4) << 10
<< setw(4) << 20 << setw(6) << 30;
예시 출력)
Start 10 20 30
(10개 전에는 2개, 20개 전에는 2개, 30개 전에는 4개의 공간이 있다.)
멤버 함수 width과 마찬가지로 setw에 대한 호출은 다음 항목 출력에만 적용되지만,

setw에 대한 여러 호출을 포함하는 것이 width에 대한 여러 호출보다 쉽다.

 

setprecision
조정자 setprecision은 멤버 함수 정밀도와 동일한 작업을 수행한다.

그러나 setw 조정자를 호출하는 것과 유사한 방식으로

삽입 연산자 << 뒤에 정밀도를 설정하기 위한 호출이 작성된다.

예시)

다음은 setprecision 호출로 표시된 소수점 뒤의 자릿수를 사용하여 나열된 숫자를 출력한다:
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout << "$" << setprecision(2) << 10.3 << endl
         << "$" << 20.5 << endl;
이전 구문은 다음과 같은 출력을 생성한다:
$10.30
$20.50
조정자 setprecision을 사용하여 소수점 이후의 자릿수를 설정할 경우,

멤버 함수 precision의 경우와 마찬가지로

setprecision 또는 sprecision에 대한 다른 호출로 다른 번호로 재설정할 때까지 설정은 유효하다.

 

<iomanip>
조정자 setw 또는 setprecision 중 하나를 사용하려면 프로그램에 다음 지침을 포함해야 한다:
#include <iomanip>
using namespace std;

 

또는 다음과 같은 이름 및 네임스페이스를 지정하는 다른 방법 중 하나를 사용해야 한다:

#include <iomanip>
using std::setw;
using std::setprecision;

 

플래그 설정 저장

함수는 원치 않거나 예상치 못한 부작용을 가져서는 안 된다.

예시)

돈의 양을 출력하는 함수는 다음을 포함할 수 있다
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
함수 호출이 종료된 후에도 이러한 설정은 계속 유효하다.

이러한 부작용을 원하지 않는 경우 원래 설정을 저장했다가 복원할 수 있다.
함수 precision이 오버로딩되어 인수 없이 현재 정밀도 설정을 반환하므로 나중에 설정을 복원할 수 있다.
setf가 설정된 플래그는 저장 및 복원이 쉽다.

멤버 함수 flags는 플래그 설정을 저장했다가 복원하는 방법을 제공하기 위해 오버로딩된다.

멤버 함수 cout.flags( )는 모든 플래그 설정을 코드화하는 long 타입의 값을 반환한다.
이 긴 값을 cout.flags 인수로 사용하여 플래그를 재설정할 수 있다.

이 기법들은 cout에서와 같이 파일 출력 스트림에 대해서도 동일하게 작동한다.
예시)

이러한 설정을 저장하고 복원하는 기능은 다음과 같이 구성할 수 있다:
void outputStuff(ofstream& outStream)
{
    int precisionSetting = outStream.precision( );
    long flagSettings = outStream.flags( );
    outStream.setf(ios::fixed);
    outStream.setf(ios::showpoint);
    outStream.precision(2);
    //Do whatever you want here.
    outStream.precision(precisionSetting);
    outStream.flags(flagSettings);
}
설정을 복원하는 또 다른 방법은
cout.setf(0, ios::floatfield);
이러한 인수로 멤버 함수 setf를 호출하면 기본 setf 설정이 복원된다.

기본값은 반드시 마지막으로 변경되기 전의 설정이 아니라 기본값입이다.

또한 기본 설정값은 구현에 의존적이다.

마지막으로, precision 설정이나 setf로 설정되지 않은 설정은 재설정되지 않는다.


기타 출력 스트림 멤버 함수

디스플레이 12.6은 클래스 ostream에 대한 포맷 멤버 함수의 일부와 조정자의 일부를 요약한 것이다. 

조정자를 사용하려면 다음과 같은 것(또는 이와 유사한 것)이 필요하다:

#include <iomanip>
using namespace std;

 

디스플레이 12.6 클래스 ostream을 위한 포맷 도구

함수 서술 해당 조작기
setf(ios_Flag) 디스플레이 12.5에 설명된 대로 플래그를 설정합니다 setiosflags(ios_Flag)
unsetf(ios_Flag) 미설정 플래그 resetiosflags(ios_Flag)
setf(0, ios::floatfield) 기본 플래그 설정을 복원합니다 None
precision(int) 부동 소수점 숫자 출력의 정밀도를 설정합니다 setprecision(int)
precision( ) 현재 정밀도 설정을 반환합니다 None
width(int) 출력 필드 폭을 설정합니다. 다음 항목 출력에만 적용됩니다. setw(int)
fill(char) 출력 필드가 값 출력보다 클 때 채우기 문자를 지정합니다. 기본값은 공백입니다 setfill(char)


예: 파일 형식 정리

디스플레이 12.7의 프로그램은

입력을 rawdata.txt 파일에서 가져와서

출력을 화면과 neat.txt 파일 모두에 깔끔한 형식으로 쓴다.

프로그램은 rawdata.txt 파일에서 neat.txt 파일로 숫자를 복사하지만,

형식 지정 명령을 사용하여 숫자를 깔끔하게 쓴다.

숫자들은 한 줄에 하나씩 너비 12의 필드에 쓰여지는데,

이것은 각 숫자 앞에 빈칸과 숫자가 12칸을 차지할 정도로 충분한 빈칸이 있다는 것을 의미한다.

숫자들은 일반적인 표기법으로 쓰여진다.

즉, 전자 표기법으로 쓰여지지 않는다.

각 숫자는 소수점 뒤에 다섯 자리 숫자와 플러스 기호 또는 마이너스 기호로 쓰여진다.

프로그램은 입력 파일 스트림과 출력 파일 스트림에 대한 공식 매개 변수를 가지는 makeNeat라는 함수를 사용힌다.

 

디스플레이 12.7 출력 포맷하기

//Reads all the numbers in the file rawdata.dat and writes the numbers
//to the screen and to the file neat.dat in a neatly formatted way.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip> //Needed for setw

using std::ifstream;
using std::ofstream;
using std::cout;
using std::endl;
using std::ios;
using std::setw;

//Stream parameters must be call-by-reference parameters.
void makeNeat(ifstream& messyFile, ofstream& neatFile,
int numberAfterDecimalpoint, int fieldWidth);
//Precondition: The streams messyFile and neatFile have been
//connected to two different files. The file named messyFile contains
//only floating-point numbers.
//Postcondition: The numbers in the file connected to messyFile have
//been written to the screen and to the file connected to the stream
//neatFile. The numbers are written one per line, in fixed-point
//notation (that is, not in e-notation), with numberAfterDecimalpoint
//digits after the decimal point; each number is preceded by a plus or
//minus sign and each number is in a field of width fieldWidth. (This
//function does not close the file.)
int main( )
{
	ifstream fin;
	ofstream fout;
    
	fin.open("rawdata.txt");
	if (fin.fail( ))
	{
		cout << "Input file opening failed.\n";
		exit(1);
	}

	fout.open("neat.txt");
	if (fout.fail( ))
	{
		cout << "Output file opening failed.\n";
		exit(1);
	}
	makeNeat(fin, fout, 5, 12);
    
	fin.close( );
	fout.close( );
	cout << "End of program.\n";
	return 0;
}

//Uses <iostream>, <fstream>, and <iomanip>:
void makeNeat(ifstream& messyFile, ofstream& neatFile,
int numberAfterDecimalpoint, int fieldWidth)
{
	//setf and precision behave the same for a file
	//output stream as they do for cout.
	neatFile.setf(ios::fixed);
	neatFile.setf(ios::showpoint);
	neatFile.setf(ios::showpos);
	neatFile.precision(numberAfterDecimalpoint);
    
	cout.setf(ios::fixed);
	cout.setf(ios::showpoint);
	cout.setf(ios::showpos);
	cout.precision(numberAfterDecimalpoint);
    
	double next;
	while (messyFile >> next) //Satisfied if there is a next number to read
	{
		cout << setw(fieldWidth) << next << endl;
		neatFile << setw(fieldWidth) << next << endl;
		//Works the same for file output
		//streams as it does for cout
	}
}

 

샘플 대화 상자

Rawdata.txt

(프로그램에 의해 변경되지 않음)

10.37       -9.89897
2.313    -8.950     15.0
   7.33333    92.8765
-1.237568432e2

 

neat.txt

(프로그램 실행 후)

+10.37000      
-9.89897      
+2.31300      
-8.95000      
+15.00000      
+7.33333      
+92.87650      
-123.75684      

 

Screen Output

+10.37000      
-9.89897      
+2.31300      
-8.95000      
+15.00000      
+7.33333      
+92.87650      
-123.75684      
End of Program.

 

예: 텍스트 파일 편집

여기서 설명하는 프로그램은 파일에 적용되는 텍스트 편집의 아주 간단한 예이다.

이 프로그램을 사용하면 기존의 C 광고 자료에서 C++ 광고 자료를 자동으로 생성할 수 있다.

프로그램은 C에 대해 좋은 점을 알려주는 광고 카피가 들어 있는 파일에서 입력을 가져와

C++에 대해 비슷한 광고 카피를 다른 파일에 쓴다.

cad.txt: C 광고 카피가 들어 있는 파일

cppad.txt: C++ 광고 카피를 받는 새 파일

프로그램은 12.8에 표시되어 있다.

프로그램은 단순히 cad.txt 파일의 모든 문자를 읽고

cppad.txt 파일에 문자를 복사한다.

입력 파일에서 대문자 'C'를 읽으면

프로그램이 출력 파일에 문자열 "C++"를 쓴다는 점을 제외하고

모든 문자는 그대로 복사된다.

이 프로그램은 입력 파일에서 문자 'C'가 나올 때마다 C 프로그래밍 언어의 이름을 붙인다고 가정한다.

따라서 이 변경 사항이 바로 업데이트된 광고 카피를 생성하는 데 필요한 사항이다.


프로그램이 입력 파일에서 문자를 읽고 출력 파일에 문자를 쓸 때 줄이 끊어짐이 유지됨을 유의하라.

새 줄 문자 '\n'은 다른 문자와 마찬가지로 취급된다.

멤버 함수 get로 입력 파일에서 읽고

삽입 연산자 <<을 사용하여 출력 파일을 쓴다.

우리는 공백을 읽고 싶기 때문에 (추출 연산자가 아닌) 멤버 함수 get으로 입력을 읽어야 한다.

 

디스플레이 12.8 텍스트 파일 편집하기

//Program to create a file called cplusad.txt that is identical
//to the filecad.txt except that all occurrences of 'C' are replaced
//by "C++". Assumes that the uppercase letter 'C' does not occur in
//cad.txt except as thename of the C programming language.
#include <fstream>
#include <iostream>
#include <cstdlib>
using std::ifstream;
using std::ofstream;
using std::cout;

void addPlusPlus(ifstream& inStream, ofstream& outStream);
//Precondition: inStream has been connected to an input file with open.
//outStream has been connected to an output file with open.
//Postcondition: The contents of the file connected to inStream have been
//copied into the file connected to outStream, but with each 'C' replaced
//by "C++". (The files are not closed by this function.)

int main( )
{
	ifstream fin;
	ofstream fout;
    
	cout << "Begin editing files.\n";
    
	fin.open("cad.txt");
	if (fin.fail( ))
	{
		cout << "Input file opening failed.\n";
		exit(1);
	}
    
	fout.open("cppad.txt");
	if (fout.fail( ))
	{
		cout << "Output file opening failed.\n";
		exit(1);
	}
    
	addPlusPlus(fin, fout);
	fin.close( );
	fout.close( );
    
	cout << "End of editing files.\n";    
	return 0;
}
void addPlusPlus(ifstream& inStream, ofstream& outStream)
{
	char next;
    
	inStream.get(next);
	while (! inStream.eof( ))
	{
		if (next = = 'C')
			outStream << "C++";
		else
			outStream << next;
            
		inStream.get(next);
	}
}

 

샘플 대화 상자

cad.txt

(프로그램에 의해 변경되지 않음)

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.

 

cppad.txt

(프로그램 실행 후)

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.

 

Screen Output

Begin editing files.
End of editing files.

 

12.3 스트림 계층구조 : 상속에 대한 미리보기

클래스를 구성하는 매우 유용한 방법 중 하나는 "derived from(파생된)" 관계에 의한 것이다.

우리가 한 클래스가 다른 클래스에서 파생되었다고 말할 때

우리는 다른 클래스에서 특징을 추가하여 파생된 클래스를 얻었다는 것을 의미한다.

예를 들어 입력 파일 스트림의 클래스는 open와 close와 같은 추가 멤버 함수를 추가하여 모든 입력 스트림의 클래스에서 파생된다.

스트림 cin은 모든 입력 스트림의 클래스에 속하지만

open와 close라는 이름의 멤버 함수가 없기 때문에

입력 파일 스트림의 클래스에는 속하지 않다.

이 절에서는 미리 정의된 스트림 클래스에 대해 생각하고 구성하는 방법으로 파생된 클래스의 개념을 소개한다.

(14장에서는 파생된 클래스의 아이디어를 사용하여 자신의 클래스를 정의하는 방법을 보여준다.)

 

스트림 클래스 간 상속

미리 정의된 스트림 cin과 입력 파일 스트림은 모두 입력 스트림이다.

그래서 그들은 어떤 의미에서는 유사하다.

예를 들어, 추출 연산자인 >>를 두 종류의 스트림과 함께 사용할 수 있다.

반면에 입력 파일 스트림은 멤버 함수 open을 사용하여 파일에 연결할 수 있지만

스트림 cin에는 open이라는 이름의 멤버 함수가 없다.

입력 파일 스트림은 cin과 비슷하지만 다른 종류의 스트림이다.

입력 파일 스트림은 ifstream의 종류이다.

객체 cin은 클래스 istream의 객체이다('f' 없이 철자).

클래스 ifstream과 istream은 서로 다르지만 밀접하게 관련된 유형이다.

클래스 ifstream은 클래스 istream의 파생 클래스이다.

그것이 무엇을 의미하는지 알아보겠다.

 

파생 클래스
일부 클래스 D가 다른 클래스 B의 파생 클래스(derived class)라고 할 때 

클래스 D는 클래스 B의 모든 기능을 가지고 있지만 추가된 기능도 가지고 있음을 의미한다.

예시)

('f'가 없는) 유형 istream의 모든 스트림은 추출 연산자 >>와 함께 사용할 수 있다.

클래스 ifstream ('f'가 있는)는 클래스 istream의 파생 클래스이므로

유형 ifstream의 개체는 추출 연산자 >>와 함께 사용할 수 있다.

클래스 ifstream의 개체는 유형 istream의 개체의 모든 속성을 가지고 있다.

특히 클래스 ifstream의 개체는 유형 istream의 개체이기도 하다.


그러나 ifstream에는 ifstream의 개체가 ifstream의 개체보다 더 많은 작업을 수행할 수 있도록 기능이 추가되었다.

예시)

하나의 추가된 기능은 ifstream의 유형 스트림을 함수가 열려 있는 상태에서 사용할 수 있다는 것이다.

스트림 cin은 ifstream의 유형만 있고 ifstream의 유형은 아니다.

함수가 열려 있는 상태에서는 cin을 사용할 수 없다.

ifstream과 istream 클래스 간의 관계는 대칭적이지 않다.

ifstream의 모든 개체는 ifstream의 유형이지만(파일 입력 스트림은 입력 스트림이지만)

ifstream의 개체는 ifstream의 유형일 필요가 없다.
파생된 계급에 대한 아이디어는 정말로 꽤 흔하다.

 

일상생활에서 얻은 예는 아이디어를 더 명확하게 만드는 데 도움이 될 수 있다.

예)

모든 컨버터블의 계급은 모든 자동차의 계급의 파생된 계급이다.

모든 컨버터블은 자동차이지만 컨버터블은 단순한 자동차가 아니다.

컨버터블은 다른 종류의 자동차에는 없는 특별한 특성을 가진 특별한 종류의 자동차이다.

컨버터블을 가지고 있다면, 자동차가 열리도록 윗부분을 낮출 수 있다.

(당신은 컨버터블이 부가 기능으로 "열린" 기능을 가지고 있다고 말할 수 있다.)

 

만약 D가 클래스 B의 파생 클래스라면, 타입 D의 모든 객체도 타입 B이다.

 

컨버터블은 자동차이기도 하다.

파일 입력 스트림(클래스 ifstream의 객체) 또한 입력 스트림(클래스 istream의 객체)이기도 하다.

따라서 ifstream을 사용하는 것이 아니라 함수 매개변수의 유형으로 istream을 사용하면 더 많은 객체가 매개변수에 연결될 수 있다.

 

매개변수의 유형(및 함수 이름)만 다른 다음 두 가지 함수 정의를 생각해 보자:
void twoSumVersion1(ifstream& sourceFile) //ifstream이 'f'인 경우
{
    int n1, n2;
    sourceFile >> n1 >> n2;
    cout << n1 << " + " << n2 << " = " << (n1 + n2) << endl;
}
그리고.
void twoSumVersion2(istream& sourceFile) //'f'가 없는 상태에서 istream입니다
{
    int n1, n2;
    sourceFile >> n1 >> n2;
    cout << n1 << " + " << n2 << " = " << (n1 + n2) << endl;
}
twoSumVersion1의 경우 인수는 ifstream 형식이어야 한다.

따라서 fileIn이 파일에 연결된 파일 입력 스트림이면
twoSumVersion1(fileIn);
합법이지만
twoSumVersion1(cin); //ILLEGAL
cin은 ifstream 유형이 아니기 때문에 합법적이지 않다.

개체 cin은 스트림일 뿐이고 istream 유형일 뿐이다.

cin은 파일 입력 스트림이 아니다.
twoSumVersion2 기능은 더 다양하다.

다음 두 기능은 모두 합법적이다:
twoSumVersion2(fileIn);
twoSumVersion2(cin);

 

교훈은 명확하다.

가능할 때마다 ifstream이 아니라 istream을 매개변수 유형으로 사용하라.

매개변수 유형을 선택할 때는 가능한 가장 일반적인 유형을 사용하라.

매개 변수 유형 ifstream 대신에 항상 매개 변수 유형 istream을 사용할 수는 없다.

매개 변수 유형 ifstream을 사용하여 함수를 정의하면 해당 매개 변수는 istream 멤버 함수만 사용할 수 있다.

특히 함수를 열고 닫을 때 함수를 사용할 수 없다.

함수 정의 밖에서 멤버 함수에 대한 모든 호출을 열고 닫을 수 없다면 ifstream의 매개 변수를 사용해야 한다.

 

ostream과 ofstream
지금까지 입력 스트림에 대한 두 가지 클래스에 대해 논의했다:

istream과 그것의 파생된 클래스 ifstream이다.

출력 스트림의 상황도 비슷하다.

1장에서는 클래스 ostream에 속하는 출력 스트림 cout과 cerr을 소개했다.

이 장에서는 ('f'인) 클래스 of stream에 속하는 파일 출력 스트림을 소개했다.

클래스 ostream은 모든 출력 스트림의 클래스이다.

스트림 cout과 cerr은 유형 ostream이지만 스트림 유형은 아니다.

cout이나 cerr과 대조적으로 출력 파일 스트림은 스트림 유형이라고 선언된다.

출력 파일 스트림의 클래스 ofstream는 클래스 ostream의 파생 클래스이다.

 

예를 들어 다음 함수는 인수로 주어진 출력 스트림에 "Hello"라는 단어를 쓴다.

void sayHello(ostream& anyOutStream)
{
    anyOutStream << "Hello";
}

다음 호출 중 첫 번째 호출은 화면에 "Hello"를 기록하고, 

두 번째 호출은 외부 파일 이름 file.txt로 파일에 "Hello"를 기록한다.

ofstream fout;
fout.open("afile.txt");
sayHello(cout);
sayHello(fout);

출력 파일 스트림은 스트림 유형 및 ostream 유형이다.

 

파생 클래스는 종종 상속(inheritance)과 가족 관계의 은유를 사용하여 논의된다.

클래스 D가 클래스 B의 파생 클래스(derived class)이면

클래스 D를 클래스 B의 자식(child),

클래스 B를 클래스 D의 부모(parent)라고 한다.

파생 클래스는 해당 부모 클래스의 멤버 함수를 상속한다(inherit)고 한다.

예) 모든 컨버터블은 모든 자동차의 클래스로부터 4개의 휠을 가지고 있다는 사실을 상속하고

모든 입력 파일 스트림의 클래스로부터 추출 연산자 >>를 상속한다.

이것이 파생 클래스의 주제를 종종 상속이라고 부르는 이유이다.

 

예: 또 다른 newline 함수

스트림 함수를 더 다용도로 만들 수 있는 예로, 

디스플레이 9.2에서 정의한 newLine 함수를 생각해 보자.

이 함수는 사전 정의된 스트림 cin에서 입력되는 키보드 입력에 대해서만 작동한다.

디스플레이 9.2의 newLine 함수에는 인수가 없다.

다음으로 입력 스트림에 대한 형식 매개 변수인 istream 유형을 갖도록 newLine 함수를 다시 작성했다.
//Uses <iostream>:
void newLine(istream& inStream)
{
    char symbol;
    do
    {
        inStream.get(symbol);
    } while (symbol != '\n');
}

 

이제 프로그램에 newLine이라는 새로운 버전의 함수가 포함되어 있다고 가정한다.

프로그램이 fin(입력 파일에 연결된)이라는 입력 스트림에서 입력을 가져오는 경우,

다음은 입력 파일에서 현재 읽고 있는 라인에 남아 있는 모든 입력을 폐기한다:
newLine(fin);

 

프로그램이 키보드에서 일부 입력을 읽고 있는 경우 다음을 수행하면 

키보드에서 입력한 나머지 입력 줄이 폐기된다:
newLine(cin);

 

프로그램에 fin 또는 cin과 같은 스트림 인수를 사용하는 이전의 다시 작성된 버전의 newLine만 있는 경우 

스트림 이름이 cin일지라도 항상 스트림 이름을 지정해야 한다.

 

그러나 오버로딩 덕분에 두 가지 버전의 newLine 기능을 동일한 프로그램에서 가질 수 있다.

디스플레이 9.2에 인수가 제공되지 않는 버전과

한 가지 유형 인수가 있는 버전은 방금 정의한 스트림이다.

newLine의 두 정의가 모두 있는 프로그램에서 다음 두 가지 호출은 동등하다:
newLine(cin);
그리고.
newLine( );
newLine 기능의 두 가지 버전이 실제로 필요한 것은 아니다.

한 가지 타입의 인수가 있는 버전은 당신이 필요로 하는 모든 것을 충족시킬 수 있다.

그러나 키보드 입력은 매우 자주 사용되기 때문에

많은 프로그래머들은 키보드 입력에 인수가 없는 버전이 편리하다고 생각하다.

 

newLine 함수의 두 가지 오버로딩 버전을 갖는 것에 대한 대안은 기본 인수를 사용하는 것이다(4장에서 논의된 바와 같이).

다음 코드에서 우리는 newLine 함수를 세 번째로 다시 작성했다.
//Uses :
void newLine(istream& inStream = cin)
{
    char symbol;
    do
    {
        inStream.get(symbol);
    } while (symbol != '\n');
}
우리가 이 함수를 다음과 같이 부른다면
newLine( );
형식 매개변수는 기본 인수 cin을 사용한다.

만약 우리가 이것을 다음과 같이 부른다면
newLine(fin);
공식 매개 변수는 인수 fin을 사용한다.
이 newLine 함수를 사용하는 대안은 9장에서 설명한 함수 ignore를 사용하는 것이다.

함수 ignore는 모든 입력 파일 스트림의 멤버이자 cin의 멤버이다.

 

 

스트림 매개변수의 활용성 향상
입력 스트림을 인수로 사용하는 함수를 정의하고 

어떤 경우에는 해당 인수가 cin이고 다른 경우에는 입력 파일 스트림이 되기를 원하는 경우에는 

istream 유형의 공식 매개 변수를 사용하자('f'가 없음).

그러나 입력 파일 스트림은 istream 유형의 인수로 사용되더라도

여전히 ifstream('f'가 있음)의 형식임을 선언해야 한다.


마찬가지로 출력 스트림을 인수로 사용하는 함수를 정의하고, 

어떤 경우에는 해당 인수를 cout하고 다른 경우에는 출력 파일 스트림을 사용하려면 

ostream 유형의 형식 매개 변수를 사용하십시오.

그러나 출력 파일 스트림은 ostream 유형의 인수로 사용되더라도

여전히 stream 유형('f'가 있는)으로 선언되어야 한다.

istream 또는 ostream 유형의 스트림 매개 변수는 열거나 닫을 수 없다.

이러한 객체를 함수에 전달하기 전에 열고 함수 호출 후에 닫자.


스트림 클래스는 스트림이고 ostream은 iostream 라이브러리에 정의되어 std 네임스페이스에 배치된다.

코드를 사용할 수 있게 하는 한 가지 방법은 다음과 같다:

#include <iostream>
using std::istream;
using std::ostream;

 

stringstream 클래스를 사용하여 문자열 구문 분석

상속의 또 다른 예로 stringstream 클래스를 살펴볼 수 있다.

이 클래스는 iostream 클래스에서 파생된 것이다.

iosteam 클래스는 다시 istream 클래스에서 파생된 것이다.

stringstream 클래스는 istream에서 상속되는 >> 연산자를 사용하여 문자열을 조작할 수 있다.

이는 파일, 키보드 입력 및 콘솔 출력을 처리하기 위해

이미 학습한 것과 동일한 형식으로 문자열을 조작할 수 있음을 의미한다.

stringstream 클래스는

다른 데이터 유형의 변수에서 문자열을 생성해야 할 때나

문자열에서 다른 데이터 유형의 변수를 읽어야 할 때 유용하다.

 

클래스를 사용하려면 먼저 다음을 포함해야 한다:
#include <sstream>
using std::stringstream;

 

다음으로 stringstream 유형의 객체를 만든다:
stringstream ss;

 

stringstream을 지우고 빈 문자열로 초기화하려면 다음 두 가지 명령을 사용한다.

stringstream에 저장될 수 있는 오류 상태를 지우기 위해서는 clear 함수가 필요하다.

stringstream을 초기 문자열로 설정하려면 str 함수를 사용한다.

필요하다면 다른 문자열로 초기화할 수 있지만,

다음 코드에서는 stringstream 객체를 빈 문자열로 설정한다.
ss.clear( );
ss.str (");


다른 변수에서 문자열 스트림을 만들려면

스트림 삽입 연산자를 사용하여 cout할 변수를 출력하는 것처럼 사용한다.

변수를 콘솔에 표시하는 대신 문자열 스트림 개체에 추가된다.

예시)

int 변수 num이 10으로 설정되고 char 변수 c가 'x'로 설정되면 다음 코드는 "x 10"을 ss에 삽입한다.
ss << c << " " << num;

 

str( ) 메서드를 사용하여 stringstream의 값을 문자열로 반환할 수 있다.
string s;
s = ss.str( ); // Sets s to the string "x 10"

 

문자열에서 변수를 추출하려면 

stringstream 개체에 문자열을 추가한 다음 

스트림 추출 연산자를 사용하여 마치 cin에서 변수를 읽는 것처럼 사용한다. 

키보드에서 값을 읽는 대신 문자열에서 값을 읽을 것이다. 

예를 들어 문자열 스트림 변수 ss가 "x 10"으로 설정되어 있으면 다음 코드로 변수를 다시 읽을 수 있다:
// ss = "x 10"이면 c는 'x'로 설정되고 num은 10으로 설정된다
ss >> c >> num;
가장 간단한 형태에서는 stringstream을 사용하여 단일 숫자 값을 문자열로 변환하거나 그 반대의 경우도 가능하다. 


stringstream 클래스를 사용하는 예는 화면 12.9에 나와 있다.

이 예제에서 우리는 사람의 이름과 점수를 포함하는 문자열로 시작한다.

예를 들어, 루이지가 70, 100, 90인 세 개의 점수를 가지고 있다면 문자열은 "루이지 70 100 90"이다.
프로그램은 stringstream 추출 연산자를 사용하여 이름과 각 점수를 정수로 읽는다.

점수를 합산하고 평균을 계산한 다음 stringstream 삽입 연산자를 사용하여 이름과 평균을 문자열에 넣는다.

예제에서 결과 문자열은 "이름:루이지 평균:86"이다.

 

디스플레이 12.9 stringstream 클래스 시연

//Demonstration of the stringstream class. This program takes
//a string with a name followed by scores. It uses a
//stringstream to extract the name as a string, the scores
//as integers, then calculates the average score. The name
//and average are placed into a new string.
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main( )
{
	stringstream ss;
	string scores = "Luigi 70 100 90";

	// Clear the stringstream
	ss.str("");
	ss.clear();

	//Put the scores into the stringstream
	ss << scores;

	// Extract the name and average the scores
	string name = "";
	int total = 0, count = 0, average = 0;
	int score;
	ss >> name; //Read the name
	while (ss >> score) // Read until the end of the string
	{
		count++;
		total += score;
	}
	if (count > 0)
	{
		average = total / count;
	}
	// Clear the stringstream
	ss.clear();
	ss.str("");
	// Put the name and average into the stringstream
	ss << "Name: " << name << " Average: " << average;
    
	// Output as a string
	cout << ss.str() << endl;
    
	return 0;
}

 

샘플 대화 상자

Name: Luigi Average: 86


12.4 파일에 대한 랜덤 액세스
이 장의 앞 절에서 설명한 파일에 대한 순차 접근 스트림은 C++에서 파일 접근에 가장 많이 사용되는 스트림이다.

하지만 매우 큰 데이터베이스의 레코드에 매우 빠른 접근이 필요한 응용 프로그램들은

파일의 특정 부분에 대한 일종의 임의 접근을 필요로 한다.

이러한 응용 프로그램은 전문 데이터베이스 소프트웨어를 사용하여 수행하는 것이 가장 좋다.

하지만 아마도 당신은 그런 패키지를 C++로 작성해야 하는 과제를 부여받거나,

아니면 단지 그런 작업들이 C++에서 어떻게 수행되는지 궁금할 뿐이다.

C++는 프로그램이 파일의 임의 위치에서 읽고 쓸 수 있도록 파일에 대한 임의 접근을 제공한다.

이 절에서는 파일에 대한 이 임의 접근 방법을 간략하게 소개한다.

이것은 파일에 대한 임의 접근 방법에 대한 전체 자습서는 아니지만,

사용되는 메인 스트림 클래스의 이름과 당신이 직면할 중요한 문제들을 알려줄 것이다.

 

C++로 파일을 읽고 쓸 수 있게 하려면

<fstream> 라이브러리에 정의된 스트림 클래스 fstream을 사용한다.

fstream의 정의는 std 네임스페이스에 있다.

 

파일을 열고 클래스 fstream의 스트림에 연결하는 것에 대한 자세한 내용은

fstream이 열려는 두 번째 인수를 가지고 있다는 점을 제외하고는

기본적으로 ifstream 및 ofstream 클래스에 대해 설명한 것과 동일하다.

이 두 번째 인수는 스트림이 입력, 출력 또는 입력과 출력 모두에 사용되는지 여부를 지정한다.

예를 들어 "stuff"라는 이름의 파일에 입력과 출력을 모두 수행하는 프로그램은 다음과 같이 시작할 수 있다:
#include <fstream>
using namespace std;
int main( )
{
    fstream rwStream;
    rwStream.open("stuff", ios::in | ios::out);


원하는 경우 이전 행 중 마지막 두 행 대신 다음을 사용할 수 있다:
fstream rwStream("stuff", ios::in | ios:out);


그 후 프로그램은 스트림 fstream을 사용하여 파일 "stuff"을 읽을 수 있고, 

같은 스트림을 사용하여 파일 "stuff"에 쓸 수도 있다.

읽기에서 쓰기로 또는 쓰기에서 읽기로 변경할 때 파일을 닫았다가 다시 열 필요가 없다.

또한 파일 내의 임의의 위치에 대한 읽기 및 쓰기 권한을 임의로 가질 수 있다.

그러나 다른 복잡한 문제가 있다.


fstream을 통해 랜덤 액세스로 읽기 및 쓰기를 할 때 적어도 두 가지 문제가 발생한다.

(1) 일반적으로 문자의 유형이나 배열을 사용하여 바이트 단위로 작업하고 유형 변환을 자체적으로 처리해야 하며,

(2) 일반적으로 각 읽기 또는 쓰기 전에 포인터(읽기 또는 쓰기 시작 위치를 표시함)를 위치시켜야 한다.

 

위치를 찾고 파일의 한 부분을 새로운 데이터로 대체한다는 제약 조건은

이러한 랜덤 액세스 I/O가 대부분 (구조나 클래스의 형태로) 레코드를 읽거나 쓰는 것으로 수행된다는 것을 의미한다.

포인터의 각 위치 지정 후에 하나의 레코드(또는 일정한 수의 레코드)를 읽거나 쓴다.
각 fstream 개체에는 seekp라는 멤버 함수가 있다.

멤버 함수 seekp: 데이터를 기록하고자 하는 위치에 put-point를 배치하는 데 사용된다.

함수 seekp는 다음에 기록할 첫 번째 바이트의 주소인 단일 인수를 사용한다.
파일의 첫 번째 바이트는 0으로 번호가 매겨진다.

예시)

fstream rwStream과 연결된 파일의 포인터를 1000번째 바이트에 위치시키려면 호출은
다음과 같이:
rwStream.seekp(1000);

 

sizeof

물론 레코드 하나에 필요한 바이트 수를 알아야 한다.

sizeof 연산자: 클래스나 구조물의 개체에 필요한 바이트 수를 결정하는 데 사용될 수 있다.

실제로 sizeof는 모든 유형, 개체 또는 값에 적용될 수 있다.

인수의 크기를 바이트 단위로 반환한다.

sizeof 연산자는 핵심 C++ 언어의 일부이며 명령어를 포함하거나 사용할 필요가 없다.

호출 샘플은 다음과 같다:
sizeof(s) (여기서 s is string s = "Hello";)
sizeof(10)
sizeof(double)
sizeof(MyStruct)(여기서 MyStruct는 정의된 유형)
이들 각각은 인수의 크기를 바이트 단위로 제공하는 정수를 반환한다.

 

MyStruct 유형의 레코드만 포함하는 파일에서

MyStruct 유형의 100번째 레코드에 풋-포인터를 위치시키려면

seekp의 호출은 다음과 같다
rwStream.seekp(100*sizeof(MyStruct) - 1);
멤버 함수 seekg: 다음 바이트의 읽기("getting")가 발생할 위치를 표시하기 위해
get-point를 지정하는 데 사용된다.

seekp와 완전히 유사하다.
우리가 보여준 설정으로, 

 

당신은 "stuff" 파일에 쓰고 "stuff" 파일에서 "stuff" 파일을 읽을 수 있고, 

"stuff" 파일에서 멤버 함수를 넣고 얻을 수 있다.
여러 바이트를 쓸 수 있는 멤버 기능 쓰기와 여러 바이트를 읽을 수 있는 멤버 기능 읽기도 있다.

 

이론적으로, 당신은 이제 임의 접근 파일 입출력을 할 수 있을 만큼 충분히 알고 있다.

사실 이것은 단지 관련된 것을 맛보기 위한 것일 뿐이다.

이 섹션은 일반적인 방법으로 당신에게 그것의 내용을 알려주기 위해 고안되었다.

임의 접근 파일 입출력의 실제 프로그래밍을 하고자 한다면, 당신은 좀 더 고급스럽고 전문적인 책을 참고해야 한다.

'프로그래밍 공부 > OOP' 카테고리의 다른 글

14장 상속  (0) 2023.11.29
13장 재귀  (1) 2023.11.28
11장 분할 컴파일 및 네임스페이스  (1) 2023.11.26
10장 포인터와 동적 배열  (1) 2023.11.26
9장 문자열  (1) 2023.11.25