콘텐츠로 이동

Chapter 16. C++ 파일 입출력

'명품 C++Programming - 황기태' 12장 학습 내용

소스코드

1절. 텍스트 파일과 바이너리 파일

2절. C++ 파일 입출력

3절. 파일 모드

4절. 텍스트 I/O와 바이너리 I/O

5절. 스트림 상태

6절. 파일 포인터

1절. 텍스트 파일과 바이너리 파일

텍스트 파일

  • 사람들이 사용하는 글자 혹은 문자들로만 구성되는 파일
  • 알파벳, 한글, 숫자, % # @ < ? 등의 기호 문자
  • '\n', '\t' 등의 특수 문자도 포함
  • 각 문자마다 문자 코드(이진수) 할당
  • 아스키 코드, 유니 코드
  • 텍스트 파일의 종류
  • txt 파일, HTML 파일, XML 파일, C++ 소스 파일, C 소스 파일, 자바 소스 파일
  • 텍스트 파일과
  • 키를 입력하면 텍스트 파일에 '\r', '\n'의 두 코드가 기록
  • 아스키 코드 표 샘플

asciicode

텍스트 파일의 내부

elvis

elvis2

바이너리 파일

  • 문자로 표현되지 않는 바이너리 데이터가 기록된 파일
  • 이미지, 오디오, 그래픽, 컴파일된 코드는 문자로 표현되지 않음
  • 텍스트 파일의 각 바이트 -> 문자로 해석
  • 바이너리 파일의 각 바이트 -> 문자로 해석되지 않는 것도 존재
  • 각 바이트의 의미는 파일을 만든 응용프로그램만이 해석 가능
  • 바이너리 파일의 종류
  • jpeg, bmp 등의 이미지 파일
  • mp3 등의 오디오 파일
  • hwp, doc, ppt 등의 확장자를 가진 멀티미디어 문서 파일
  • obj, exe 등의 확장자를 가진 컴파일된 코드나 실행 파일

바이너리 파일의 내부

uisee

hwp 파일은 바이너리 파일

  • 텍스트 정보 포함
  • 한글, 영어 문자 포함
  • 바이너리 정보 포함
  • 글자 색이나 서체 등의 문자 포맷 정보
  • 비트맵 이미지
  • 선, 원 등의 그래픽 정보
  • 왼쪽 마진, 오른쪽 마진 등 문서 포맷 정보

2절. C++ 파일 입출력

C++ 표준 파일 입출력 라이브러리

  • 스트림 입출력 방식 지원 stream

템플릿에 char 타입으로 구체화한 클래스

typedef

파일과 프로그램을 연결하는 파일 입출력 스트림

iostream

  • 연산자와 istream의 get(), read() 함수

  • 연결된 장치로부터 읽는 함수
  • 키보드에 연결되면 키 입력을, 파일에 연결되면 파일에서 입력
  • << 연산자와 ostream의 put(), write() 함수
  • 연결된 장치에 쓰는 함수
  • 스크린에 연결되면 화면에, 파일에 연결되면 파일에 출력

헤더 파일과 namespace

  • C++ 파일 입출력 라이브러리 사용
  • fstream 헤더 파일과 std 이름 공간의 선언 필요
#include <fstream>

using namespace std;

파일 입출력 모드 : 텍스트 I / O와 바이너리 I / O

  • 파일 입출력 방식
  • 텍스트 I / O와 바이너라 I / O의 두 방식
    • C++ 파일 입출력 클래스(ifstream, ofstream, fstream)는 두 방식 지원
  • 텍스트 I / O
  • 문자 단위로 파일에 쓰기, 파일에서 읽기
    • 문자를 기록하고, 읽은 바이트를 문자로 해석
  • 텍스트 파일에만 적용
  • 바이너리 I / O
  • 바이트 단위로 파일에 쓰기, 파일에서 읽기
    • 데이터를 문자로 해석하지 말고 그대로 기록하거나 읽음
  • 텍스트 파일과 바이너리 파일 모두 입출력 가능
  • 텍스트 I / O와 바이너리 I / O 입출력 시 차이점
  • 개행 문자('\n')를 다루는데 차이점 존재

<< 연산자를 이용한 간단한 파일 출력

ofstream fout;
// 파일 쓰기를 위한 스트림 생성

fout.open("song.txt");
// song.txt 파일 열기
// ofstream fout("song.txt"); 한 줄로 줄여 쓰기 가능

if(!fout){
 // fout 스트림의 파일 열기가 실패한 경우 파일 열기 실패를 처리하는 코드
 // 파일 열기 성공 검사로, operator !() 실행
 // if(!fout.is_open())과 동일
}

 int age = 21;
 char singer[] = "Kim";
 char song[] = "Yesterday";

 // 파일 쓰기 예시
 fout << age << "\n";
 // 파일에 21과 '\n'을 기록

 fout << singer << endl;
 // 파일에 "Kim"과 '\n'을 덧붙여 기록

 fout << song << endl;
 // 파일에 "Yesterday"와 '\n'을 덧붙여 기록
 fout.close();
 // 파일 닫기

song

3절. 파일 모드

파일 모드(file mode)

  • 파일 입출력에 대한 구체적인 작업 형태에 대한 지정
  • 사례
  • 파일에서 읽을 작업을 할 것인지, 쓰기 작업을 할 것인지
  • 기존 파이르이 데이터를 모두 지우고 쓸 것인지, 파일의 끝 부분에 쓸 것인지
  • 텍스트 I / O 방식인지 바이너리 I / O 방식인지

파일 모드 지정 : 파일 열 때

  • open("파일 이름", 파일 모드)
  • ifstream("파일 이름", 파일 모드)
  • ofstream("파일 이름", 파일 모드)

filemode

파일 모드 설정

void open(const char* filename, ios::openmode mode);
// mode로 지정된 파일 모드로 filename의 파일을 열고 파일 모드 지정
  • student.txt 파일에서 처음부터 읽고자 하는 경우
// case 1
ifstream fin;
fin.open("student.txt");
// case 2
ifstream fin;
fin.open("student.txt", ios::in);
  • student.txt 파일의 끝에 데이터를 저장하는 경우
ofstream fout;
fout.open("student.txt", ios::out | ios::app);
fout << "tel:01044447777";
// 기존의 stduent.txt 끝에 "tel:01044447777"을 추가하여 저장
  • 바이너리 I / O로 data.bin 파일을 기록하는 경우
fstream fbinout;
fbinout.open("data.bin", ios::out | ios:: binary);
char buf[128];
...
fbinout.write(buf, 128);
// buf에 있는 128 바이트를 파일에 기록

get()과 EOF

  • 파일의 끝에서 읽기를 시도하면 get()은 EOF(-1값)를 리턴

EOFget

get()으로 파일의 끝을 인지하는 방법

  • 방법 1
// case 1
while(true){
 int c = fin.get();
 // 파일에서 문자(바이트)를 읽음
 if(c == EOF){
  ...  // 파일의 끝을 만난 경우 이에 대응하는 코드 작성
  break;
  // while 루프문 탈출
 }
 else{
  // 읽은 문자(바이트) c 처리
 }
}
  • 방법 2
// case 2
while((c = fin.get()) != EOF){// 파일의 끝을 만나면 루프 종료
 ... // 파일에서 읽은 값 c를 처리하는 코드
}

파일의 끝을 잘못 인지하는 코드

  • EOF 값을 c에 읽어 사용한 후 다음 루프의 while 조건문에서 EOF에 도달한 사실을 알게 되기 때문에 잘못된 코드
while(!fin.eof()){
 int c = fin.get();
 // 마지막 읽은 EOF(-1) 값이 c에 리턴
 ...  // 읽은 값 c를 처리하는 코드
}

4절. 텍스트 I/O와 바이너리 I/O

텍스트 파일의 라인 단위 읽기

  • 두 가지 방법
  • istream의 getline(char*line, int n) 함수 이용
  • getline(ifstream& fin, string& line) 함수 이용
  • 라인 단위로 텍스트 파일을 읽는 코드 2가지
  • istream의 getline() 함수 이용
char buf[81];
// 한 라인이 최대 80개의 문자로 구성된다고 가정

ifstream fin("c:\\windows\\system.ini");
while(fin.getline(buf, 81){ // 한 라인이 최대 80개의 문자로 구성되었으며 끝에 '\0' 문자 추가
 ...   // 읽은 라인(buf[])을 활용하는 코드
}
  • 전역 함수 getline(ifstream& fin, string& line) 함수 이용
string line;
ifstream fin("c:\\windows\\system.ini");
while(getline(fin, line){ // 한 라인을 읽어 line에 저장한 후 파일 끝까지 반복
 ...   // 읽은 라인(line)을 활용하는 코드
}
  • 예제 4 참고 이미지
    ex4

  • 예제 4. 텍스트 파일 연결 예제
    SourceCodeChecking

  • 예제 5. istream의 getline()을 이용하여 텍스트 파일을 읽고 화면에 출력하는 예제
    SourceCodeChecking

  • 예제 6. getline(ifstream&, string&)으로 words.txt 파일을 읽고 단어 검색하는 예제
    SourceCodeChecking

바이너리 I/O

  • 바이너리 I/O 방식
  • 데이터의 바이너리 값을 그대로 파일에 저장
  • 파일의 바이너리 값을 그래도 읽어서 변수나 버퍼에 저장
  • 텍스트 파일이든 바이너리 파일이든 바이너리 I/O로 입출력 가능
  • 바이너리 I/O 모드 열기
  • ios::binary가 설정되지 않으면 디폴트가 텍스트 I/O
ifstream fin;
fin.open("desert.jpg", ios::in | ios::binary);
// 바이너리 I/O로 파일 읽기

ofstream fout("desert.jpg", ios::out | ios::binary);    // 바이너리 I/O로 파일 쓰기
fstream fsin("desert.jpg", ios::in | ios::binary);      // 바이너리 I/O로 파일 읽기
![readingline](./Image/ch15/readingline.PNG)

read()/write()로 블록 단위 파일 입출력

  • get() / put()
  • 문자 혹은 바이트 단위로 파일 입출력
  • read() / write()
  • 블록 단위로 파일 입출력
istream& read(char* s, int n);
// 파일에서 최대 n개의 바이트를 배열 s에 읽으며 파일의 끝을 만나면 읽기 중단

ostream& write(char* s, int n);
// 배열 s에 있는 처음 n개의 바이트를 파일에 저장

int gcount();
// 최근에 파일에서 읽은 바이트 수 리턴
  • 예제 8. read()로 텍스트 파일을 바이너리 I/O로 읽는 예제
    SourceCodeChecking

  • 예제 9. read()/write()로 이미지 파일 복사 예제
    SourceCodeChecking

  • 예제 10 참고 이미지
    ex10

  • 예제 10. int 배열과 double 값을 바이너리 파일에 저장하고 읽는 예제
    SourceCodeChecking

텍스트 I/O와 바이너리 I/O의 확실한 파이점

  • 파일의 끝을 처리하는 방법에는 차이가 없음
  • 텍스트 I/O든 바이너리 I/O든 파일의 끝을 만나면 EOF 리턴
  • 개행 문자 '\n'를 읽고 쓸 때 서로 다르게 작동

textbinary

텍스트 I/O와 바이너리 I/O의 실행 결과 비교

textbinary2

5절. 스트림 상태

스트림 상태 검사

  • 스트림 상태
  • 파일 입출력이 진행되는 동안 스트림(열어 놓은 파일)에 관한 입출력 오류 저장
    • 스트림 상태를 저장하는 멤버 변수 이용

streamstat

스트림 상태를 나타내는 비트 정보

streambit

스트림 상태를 검사하는 멤버 함수

streammem

6절. 파일 포인터

C++ 파일 입출력 방식

  • 순차 접근
  • 읽은 다음 위치에서 읽고 쓴 다음 위치에 쓰는 방식
  • 디폴트 파일 입출력 방식
  • 임의 접근
  • 파일 내의 임의의 위치로 옮겨 다니면서 읽고 쓸 수 있는 방식
  • 파일 포인터를 옮겨 파일 입출력

파일 포인터

  • 파일은 연속된 바이트의 집합
  • 파일 포인터
  • 파일에서 다음에 읽거나 쓸 위치를 표시하는 특별한 마크
  • C++는 알려진 파일마다 두 개의 파일 포인터 유지
  • get pointer : 파일 내 다음에 읽을 위치
  • put pointer : 파일 내 다음에 쓸 위치

파일 모드와 파일 포인터

FileMP

임의의 접근 방법

  • 파일 포인터 제어
  • 절대 위치로 이동시키는 방법과 상대 위치로 이동시키는 두 방법
// istream의 두 방법
istream& seekg(streampos pos);
// 정수 값으로 주어진 절대 위치 pos로 get pointer를 옮김

istream& seekg(streamoff offset, ios::seekdir seekbase);
// seekbase를 기준으로 offset만큼 떨어진 위치로 get pointer를 옮김
// ostream의 두 방법
ostream& seekp(streampos pos);
// 정수 값으로 주어진 절대 위치 pos로 put pointer를 옮김

ostream& seekp(streamoff offset, ios::seekdir seekbase);
// seekbase를 기준으로 offset만큼 떨어진 위치로 put pointer를 옮김
// streampos의 두 방법
streampos tellg();
// 입력 스트림의 현재 get pointer의 값 리턴

streampos tellp();
// 출력 스트림의 현재 put pointer의 값 리턴

seekbase

seekg()에 의한 get pointer의 이동 사례

seekg

  • 예제 12 참고 이미지
    ex12

  • 예제 12. 파일 크기 알아내기 예제
    SourceCodeChecking