Search
Duplicate

12장. C++ 파일 입출력

생성일
2022/05/28 04:59
태그
C++

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++
복사