12장. C++ 파일 입출력
텍스트 파일 - 문자만으로 구성된 문서 (읽을 수 있는 것)
•
특수 문자도 포함 (’\n’, ‘\t’)
•
각 문자마다 문자코드(2진수) 할당 - 아스키코드, 유니코드
•
txt 파일, HTML 파일, XML 파일, C++ 소스 파일 등
바이너리 파일 - 문자, 그림, 표, 사운드, 동영상 등으로 구성된 문서 (읽을 수 있는 것 + 읽을 수 없는 것)
바이너리 파일
•
문자로 표현되지 않는 바이너리 데이터가 기록된 파일
◦
이미지, 오디오, 그래픽 등등
•
텍스트 파일의 각 바이트 → 문자로 해석된다
•
바이너리 파일의 각 바이트 → 문자로 해석되지 않는 것도 있음
◦
각 바이트의 의미는 파일을 만든 응용프로그램 만이 해석 가능
◦
문자로 매핑되지 않는 바이너리 값
바이너리 파일의 종류
•
jpeg, bmp 등의 이미지 파일
•
mp3
•
hwp, doc, ppt
•
obj, exe
파일 입출력 스트림은 파일을 프로그램과 연결한다
•
>> 연산자와 istream의 get(), read() 함수
◦
연결된 장치로부터 읽는 함수
◦
키보드에 연결되면 키 입력을, 파일에 연결되면 파일에서 입력
•
<< 연산자와 ostream의 put(), write() 함수
◦
연결된 장치에 쓰는 함수
◦
스크린에 연결되면 화면에, 파일에 연결되면 파일에 출력
파일 입출력 모드 : 텍스트 I/O 와 바이너리 I/O
•
파일 입출력 방식
•
텍스트 I/O
◦
문자 단위로 파일에 쓰기, 파일에서 읽기
▪
문자 기록, 읽은 바이트 문자로 해석
◦
텍스트 파일에만 적용
•
바이너리 I/O
◦
바이트 단위로 파일에 쓰기, 파일에서 읽기
▪
데이터를 문자로 해석하지 않고, 있는 그대로 기록, 읽음
◦
텍스트 파일과 바이너리 파일 모두 입출력 가능
•
둘의 차이점
◦
개형 문자(’\n’)를 다루는데 있다
<< 연산자를 이용한 간단한 파일 출력
ofstream fout;
fout.open("song.txt");
if (!fout) {
}
fout << age << '\n';
fout << singer << endl;
fout.close();
C++
복사
파일 모드 (file mode)
파일 모드
•
파일 입출력에 대한 구체적인 작업 행태에 대한 지정
◦
구체적인 작업 → 읽기를 할지, 쓰기를 할지 등
파일 모드 지정 → 파일 열 때
•
open (”파일이름", 파일모드)
•
ifstream (”파일이름", 파일모드)
•
ofstream (”파일이름", 파일모드)
파일모드 | 의미 |
ios::in | 읽기 위해 파일을 연다 |
ios::out | 쓰기 위해 파일을 연다 |
ios::ate | 쓰기 위해 파일을 연다. 열기 후 파일 포인터를 파일 끝에 둔다. 파일 포인터를 옮겨 파일 내의 임의의 위체에 쓸 수 있다. |
ios::app | 파일 쓰기 시에만 적용된다. 파일 쓰기 시마다, 자동으로 파일 포인터가 파일 끝으로 옮겨져서 항상 파일의 끝에 쓰기가 이루어진다. |
ios::trunc | 파일을 열 때, 파일이 존재하면 파일의 내용을 모두 지워 파일 크기가 0인 상태로 만든다.
ios::out 모드를 지정하면 디폴트로 함께 지정된다. |
ios::binary | 바이너리 I/O 로 파일을 연다. 이 파일 모드가 지정되지 않으면 디폴트가 텍스트 I/O 이다 |
파일 모드 설정
void open(const char *filename, iost::openmode mode)
// mode로 지정된 파일 모드로 filename의 파일을 연다
C++
복사
student.txt 파일에서 처음부터 읽고자 하는 경우
ifstream fin;
fin.open("student.txt", ios::in);
C++
복사
student.txt 파일의 끝에 데이터를 저장하는 경우
ofstream fout;
fout.open("student.txt", ios::out | ios::app);
C++
복사
바이너리 I/O 로 data.bin 파일을 기록하는 경우
fstream fbinout;
fbinout.open("data.bin", ios::out | ios::binary);
C++
복사
get() 과 EOF
•
파일의 끝을 만나면 읽기를 멈추어야 하는데, get()은 파일의 끝을 어떻게 인식할까?
◦
파일의 끝에서 읽기를 시도하면 get()은 EOF(-1값)를 리턴한다
get()으로 파일의 끝을 인지하는 방법
while(true) {
int c = fin.get(); // 파일에서 문자(바이트)를 읽는다
if (c == EOF) {
... // 파일의 끝을 만난 경우. 이에 대응하는 코드를 작성
break; // while 루프에서 빠져나온다.
} else {
... // 읽은 문자(바이트) c를 처리한다
}
}
C++
복사
== 동일한 코드
while((c = fin.get()) != EOF) { // 파일의 끝을 만나면 루프 종료
... // 파일에서 읽은 값 c를 처리하는 코드
}
C++
복사
파일의 끝을 잘못 인지하는 코드
while (!fin.eof()) {
int c = fin.get(); // 마지막 읽은 EOF(-1) 값이 c에 리턴된다
... // 읽은 값 c를 처리하는 코드
}
C++
복사
→ EOF 값을 c에 읽어 사용한 후 , 다음 루프의 while 조건문에서 EOF에 도달한 사실을 알게 된다.
예제 12-4) 텍스트 파일 연결
C++
복사
텍스트 파일의 라인 단위 읽기
두 가지 방법
•
istream
getline(char* line, int n)
C++
복사
getline(ifstream& fin, string& line)
C++
복사
라인 단위로 텍스트 파일을 읽는 전형적인 코드
1) istream의 getline() 함수 이용
2) 전역 함수 getline(ifstream& fin, string& line) 함수 이용
예제 12-5) istream의 getline()을 이용하여 텍스트 파일을 읽고 화면 출력
#include <fstream>
ifstream fin("주소")
char buf[81];
while (fin.getline(buf, 81)) {
cout << buf << endl;
}
fin.close()
C++
복사
예제 12-6) getline(ifstream&, string&) 으로, words.txt 파일을 읽고 단어 검색
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void fileRead(vector<string> &v, ifstream &fin) {
string line;
while (getline(fin, line)) {
v.push_back(line);
}
}
void search(vector<string> &v, string word) {
for (int i = 0; i < v.size(); i++) {
int index = v[i].find(word);
if (index != -1)
cout << v[i] << endl;
}
}
int main() {
vector<string> wordVector;
ifstream fin("words.txt");
if (!fin) {
cout << "words.txt 파일을 열 수 없습니다." << endl;
return 0;
}
fileRead(wordVector, fin);
fin.close();
cout << "words.txt 파일을 읽었습니다." << endl;
while (true) {
cout << "검색할 단어를 입력하세요>> ";
string word;
getline(cin, word);
if (word == "exit") break;
search(wordVector, word);
}
cout << "프로그램을 종료합니다." << endl;
}
C++
복사
바이너리 I/O
•
데이터의 바이너리 값을 그대로 파일에 저장하거나
•
파일의 바이너리 값을 그대로 읽어서 변수나 버퍼에 저장하는 방식
•
ios :: binary 모드 속성 사용
ifstream fin;
fin.open("desert.jpg", ios::in | ios::binary);
ofstream fout("desert.jpg", ios::out | ios::binary);
fstream fsin("desert.jpg", ios::in | ios::binary);
C++
복사
예제 12-8) read()로 텍스트 파일을 바이너리 I/O 로 읽기
#include <fstream>
ifstream fin;
fin.open(file, ios::in | ios::binary); // 읽기 모드로 파일 열기
if (!fin) {
cout << "파일 열기 오류";
return 0;
}
int count = 0;
char s[32];
while (!fin.eof()) { // 파일 끝까지 읽는다
fin.read(s, 32); // 최대 32바이트를 읽어 배열 s에 저장
int n = fin.gcount(); // 실제 읽은 바이트 수 알아냄
cout.write(s, n); // 버퍼에 있는 n개의 바이트를 화면에 출력
count += n;
}
cout << "읽은 바이트 수는 " << count << endl;
fin.close();
C++
복사
예제 12-9) read() / write() 로 이미지 파일 복사
// 소스파일에서 목적파일로 복사하기
char buf[1024];
while (!fsrc.eof()) {
fsrc.read(buf, 1024); // 최대 1024바이트를 읽어 배열 s에 저장
int n = fsrc.gcount(); // 실제 읽은 바이트 수 알아냄
fdest.write(buf, n); // 읽은 바이트 수 만큼 버퍼에서 목적 파일에 기록
}
fsrc.close();
fdest.close();
C++
복사
예제 12-10) int배열과 double 값을 바이너리 파일에 저장하고 읽기
#include <iostream>
#include <fstream>
using namespace std;
int main() {
char* file = "주소";
ofstream fout;
fout.open(file, ios::out | ios::binary);
if (!fout) {
cout << "파일 열기 오류";
return 0;
}
int n[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
double d = 3.15;
fout.write((char*)n, sizeof(n)); // int 배열 n을 한번에 파일에 쓴다
fout.write((char*)(&d), sizeof(d)); // double 값 하나를 파일에 쓴다
fout.close();
// 배열 n과 d를 임의의 값으로 변경시킨다
for (int i = 0; i < 10; i++) n[i] = 99;
d = 8.15;
ifstream fin(file, ios::in | ios::binary);
if (!fin) { // 열기 실패 검사
cout << "파일 열기 오류";
return 0;
}
//read()로 한번에 배열을 읽는다
fin.read((char*)n, sizeof(n));
fin.read((char*)(&d), sizeof(d));
for (int i = 0; i < 10; i++)
cout << n[i] << ' ';
cout << endl << d << endl;
fin.close();
}
C++
복사
텍스트 I/O 와 바이너리 I/O의 확실한 차이점
•
파일의 끝을 처리하는 방법은 똑같다
◦
파일의 끝을 만나면 EOF 리턴
•
개행문자 ‘\n’ 를 읽고 쓸때, 서로 다르게 작동
텍스트 I/O 모드
char buf[] = {'a', 'b', '\n');
fout.write(buf, 3)
// 파일에 'a', 'b', '\r', '\n'의 4개의 바이트 저장
C++
복사
바이너리 I/O 모드
ofstream fout("주소", ios::out | ios::binary);
char buf[] = {'a', 'b', '\n'};
fout.write(buf, 3);
// 파일에 'a', 'b', '\n'의 3개의 바이트 저장
C++
복사
스트림 상태 검사
•
파일에 입출력이 진행되는 동안, 스트림(열어놓은 파일)에 관한 입출력 오류 저장
◦
스트림 상태를 저장하는 멤버 변수 이용
스트림 상태를 나타내는 비트 정보
비트 | 설명 |
eofbit | 파일의 끝을 만났을 때, 1로 세팅 |
failbit | 정수를 입력받고자 하였으나, 문자열이 입력되는 등 포맷 오류나, 쓰기 금지된 곳에 쓰기를 시행하는 등 전반적인 I/O 실패 시에 1로 세팅 |
badbit | 스트림이나 데이터가 손상되는 수준의 진단되지 않는 문제가 발생한 경우나, 유효하지 않는 입출력 명령이 주어졌을 때 1로 세팅 |
스트림 상태를 검사하는 멤버 함수
멤버 함수 | 설명 |
eof() | 파일의 끝을 만났을 때 (eofbit = 1), true 리턴 |
fail() | failbit나 badbit가 1로 세팅되었을 때, true 리턴 |
bad() | badbit이 1로 세팅되었을 때, true 리턴 |
good() | 스트림이 정상적 (모든 비트가 0)일 때, true 리턴 |
clear() | 스트림 상태 변수를 0으로 지움 |
임의 접근과 파일 포인터
C++ 파일 입출력 방식
순차 접근
•
읽은 다음 위치에서 읽고,
•
쓴 다음 위치에 쓰는 방식
•
디폴트 파일 입출력 방식
임의 접근
•
파일 내 임의의 위치로 옮겨 다니면서, 읽고 쓸 수 있는 방식
•
파일 포인터를 옮겨 파일 입출력
파일 포인터 (file pointer)
•
파일은 연속된 바이트의 집합
•
파일에서 다음에 읽거나, 쓸 위치를 표시하는 특별한 마크
C++는 열려진 파일마다, 두 개의 파일 포인터 유지
•
get pointer
◦
파일 내에 다음에 읽을 위치
•
put pointer
◦
파일 내에 다음에 쓸 위치
임의 접근 방법
파일 포인터 제어
•
절대 위치로 이동
•
상대 위치로 이동
istream& seekg(streampos pos)
// 정수 값으로 주어진 절대 위치 pos로 get pointer를 옮김
istream& seekg(streamoff offset, ios::seekdir seekbase)
// seekbase를 기준으로 offset만큼 떨어진 위치로 get pointer를 옮김
ostream& seekp(streampos pos)
// 정수 값으로 주어진 절대 위치 pos로 put pointer를 옮김
ostream& seekp(streamoff offset, ios::seekdir seekbase)
// seekbase를 기준으로 offset만큼 떨어진 위치로 put pointer를 옮김
streampos tellg()
// 입력 스트림의 현재 get pointer의 값 리턴
streampos tellp()
// 출력 스트림의 현재 put pointer의 값 리턴
C++
복사
seekbase | 설명 |
ios :: beg | 파일의 처음 위치를 기준으로 파일 포인터를 움직인다 |
ios :: cur | 현재 파일 포인터의 위치를 기준으로 파일 포인터를 움직인다 |
ios :: end | 파일의 끝(EOF) 위치를 기준으로 파일 포인터를 움직인다 |
예제 12-12) 파일 크기 알아내기
long getFileSize(ifStream& fin) {
fin.seekg(0, ios::end); // get pointer를 파일의 맨 끝으로 옮기
long length = fin.tellg(); // get pointer의 위치를 알아냄
return length; // length는 파일의 크기와 동일
}
C++
복사