본문 바로가기
Basic/C/C++

[C언어] 제 22 강 : 파일 입출력

by boxbop 2012. 1. 28.
반응형


 프로그램이라면 기본적으로 갖춰야 할 요소중에 하나가 데이터의 저장입니다. 우리가 일반적으로 접하는 거의 대부분의 프로그램에서는 데이터의 저장이라는 기능이 다양한 형태로 존재하고 있습니다. 때문에 이번 장에서는 파일에 데이터를 저장하고, 저장한 데이터를 참조하는 방법을 알아보도록 하겠습니다.

 우리가 구현한 프로그램과 참조할 데이터가 저장되어 있는 파일 사이에 데이터가 이동할 수 있는 다리를 놓아야 합니다. 컴퓨터 공학에서는 이다리를 '스트림'이라고 하지요

                                             Stream
                 [Program] <-------------------> [Hard Disk]

 파일과의 스트림을 형성하는 방법의 fopen 함수

 FILE*  fopen(const char* filename, const char* mode);
성공 시 해당 파일의 FILE 구조체 포인터, 실패 시 NULL 포인터 반환
-> 스트림을 형성할 때 호출하는 함수입니다.

ex) FILE*  fp = fopen("C:\\prog\\file.txt", "wt");
-> 위 함수의 첫 번째 전달인자는 스트림을 형성할 파일의 경로와 파일의 이름을 알려주고 두 번째 전달인자는 스트림의 종류가 텍스트 데이터를 쓰기 위한 스트림이다라고 알려주는 역할을 합니다. 쓰기모드 즉, 출력스트림이기 때문에 파일에 데이터를 쓸 수 만 있고 읽지는 못합니다. 읽기를 원한다면 "rt"를 사용하면 읽어올 수 있습니다.

 rt : 텍스트 입력용 스트림
 wt : 텍스트 출력용 스트림

 fp라는 파일 구조체 포인터에 스트림을 개방해주었다면 이 포인터 변수를 가지고 활용을 해야겠죠? 실제로 스트림을 형성해서 파일에 데이터를 전송해보고자 한다면 다음과 같은 문장을 사용할 수 있습니다.

 fputc('A', fp);
->fputc 함수를 통해 'A'라는 텍스트를 fp가 가리키는 파일에 저장시킵니다.

cf)참고로 스트림을 형성하는 경우에는 스트림을 형성할 파일이 존재하지 않으면 자동으로 파일이 생성됩니다.

 그렇다면 형성된 스트림의 소멸은 어떻게 해야할까요? fclose 라는 함수가 담당합니다. fopen함수의 반대 기능을 제공합니다.

 int  fclose(FILE* stream);
성공 시 0, 실패시 EOF를 반환
->파일을 닫는다.

 이렇게 fclose 함수의 호출을 통해서 개방되었던 파일을 닫아줘야 하는 이유는 운영체제가  할당한 자원의 반환, 버퍼링 되었던 데이터의 출력에 있습니다. 사용이 끝난 파일은 곧바로 fclose 함수를 호출해는 것이 좋습니다. 사실 파일 스트림을 형성하는 주체는 운영체제입니다. 여기서 운영체제는 스트림의 형성을 위해 시스템의 자원(메모리)을 사용합니다. 때문에 파일을 닫아주지 않으면 할당된 체로 남아있어 자원의 손실로 이어지기때문이죠. 또한 프로그램과 파일의 실제 입출력은 프로그램과 하드디스크 사이의 '버퍼'라는 공간이 존재합니다. 우리가 fputc와 같은 함수의 호출로 데이터를 전송한다고 해서 파일에 바로 저장되는 것이 아니라 일단 버퍼에 저장이 된다음에 운영체제가 정해놓은 버퍼링 방식에 따라 파일이 저장됩니다. fclose 함수의 호출을 통해서 파일을 닫아주면 메모리 버퍼에 저장되어 있던 데이터가 파일로 이동이 되면서 버퍼는 비워지게 됩니다.

 버퍼를 비우고 싶을때 물론 스트림을 종료하지않고말이죠. 그럴때 사용하는 함수가 ffulsh 함수입니다.

 int  fflush(FILE* restrict stream);
성공시 0, 실패 시 EOF 반환
->버퍼를 비워주는 함수

 그러나 fflush함수에서 중요한 것은 입력 스트림을 비우는 요도로 사용할 수 없습니다. 오직 출력 스트림을 비우는 용도로만 사용이 가능하죠^-^

 FILE* fp = fopen("C:\\prog\\test.txt", "rt");
 fflush(fp); //이것은 잘못된 코드

 FILE* fp = fopen("C:\\prog\\test.txt", "wt");
 fflush(fp); //올바른 코드

 출력 버퍼가 비워진다는 것은 출력 버퍼에 저장된 데이터가 느야 소멸되는 것이 아니라, 출력 버퍼에 저장된 데이터가 목적지에 전송이 되어 비워지는 것을 의미합니다.

 이번에는 스트림의 모드에 대하여 살펴보겠습니다. fopen의 두 번째 인자에 전달되는 정보입니다.

 r  :  읽기                                          r+  :  읽기/쓰기
w  :  쓰기                                         w+  :  읽기/쓰기
a  :  파일의 끝에 덧붙여 쓰기               a+  :  읽기/덧붙여 쓰기

 r, r+ 는 파일이 없다면 에러를 발생하지만 나머지는 파일이 없다면 파일을 생성해버립니다. 쉽게 정리하자면 + 는 읽기, 쓰기가 모두 가능한 스트림의 형성을 의미하고 a는 덧붙여 쓰기가 가능한 스트림을 형성합니다.

 근대 우리가 지금까지 사용한 fopen함수를 보면 wt 나 rt를 사용했죠? t 는 무슨 의미일까요? 텍스트 모드라는 뜻 입니다. 바이너리 모드에서는 b를 사용하죠.

  텍스트 모드 : rt, wt, at, r+t, w+t, a+t
  바이너리 모드 : rb, wb, ab, r+b, w+b, a+b

 바이너리 모드에 대해서는 따로 설명하지 않겠습니다. 텍스트모드가 일반적이고 자주사용하니까 굳이 설명안드려도 될 것 같습니다~

 일단 파일 입출력에서 사용하는 함수를 정리해보도록 하겠습니다.

[문자]
 in fgetc(FILE* stream);     //입력
 int getc(FILE* stream);     //입력
 char* fputc(int c, FILE* stream);    //출력
 char* putc(int c, FILE* stream);     //출력

[문자열]
 char* fgets(char* s, int n, FILE* stream);    //입력
 int fputs(const char* s, FILE* stream);     //출력

ex) fgets (buf, 100, fp); //buf는 배열이름, fp는 FILE구조체 포인터
->fp가 가리키는 파일로부터 최대 길이가 100인 문자열을 읽어서 배열 buf에 저장한다.

ex) fputs(buf, fp);
->buf에 저장된 문자열을 fp가 가리키는 파일에 저장한다.

 파일의 끝을 확인 : feof 함수
 int feof(FILE* stream); 
파일의 끝에 도달한 경우 0이 아닌 값 반환
->포인터가 가리키는 파일이 끝에 도달했는지 판단


 서식에 따른 데이터 출력 : fprintf 함수
char name[10] = "boxbop";
char sex = 'M';
int age = 10;
fprintf(fp, "%s %c %d", name, sex, age);    //fp는 FILE 구조체 포인터
->입출력의 대상은 콘솔이 아닌 파일입니다. 이렇게 만들어진 문자열 (boxbop M 10)은 fp가 가리키는 파일에 저장이 됩니다.

 서식에 따른 데이터 입력 : fscanf 함수
char name[10];
char sex;
int age;
fscanf(fp, "%s %c %d", name, &sex, &age);    //fp는 FILE 구조체 포인터
->fp로 부터 파일을 읽어들입니다. 실패하면 EOF를 반환합니다. 기존에 존재하는 파일로부터 데이터를 읽어들이는 겁니다. 사용자로부터 입력받는게 아니에요~ 주의하세요

 이번장은 여기까지 하도록 하겠습니다~ 다음 장이 마지막 장이 되겠네요~
다음 장으로 넘어가도록 하겠습니다^^



 
반응형