프로그램이 성공적으로 파일을 열면, fopen()은 파일 포인터(file pointer)를 리턴한다. 다른 입출력 함수들은 그 파일 포인터를 그 파일을 나타내는데 사용한다. 파일 포인터(FILE *fp)는 FILE을 가리키는 포인터 이다.
FILE은 stdio.h에 정의 되어있는 유도 데이터형이다. 포인터 fp는 실제 파일을 가리키지 않는다. 대신에 그 파일의 입출력에 사용되는 버퍼정보를 포함하여, 그 파일에 관한 정보가 들어있는 데이터 객체를 가리킨다.
표준 라이브러리에 있는 입출력 함수들은 버퍼를 사용하기 때문에, 버퍼가 어디에 있는지 알 필요가 있다. 또한 버퍼가 얼마나 채워져 있는지, 어느 파일을 사용하는지도 알 필요가 있다. 이 정보들은, 필요할때 함수들이 버퍼를 다시 채우거나 비우는 것을 가능하게 한다. fp가 가리키는 데이터 객체가 그 모든 정보를 가지고 있다.
fopen()함수는 그 파일을 열 수 없을때 (stdio.h에 정의되어있는) 널 포인터를 리턴한다. 몇가지 예를 든다면, fopen()함수는 디스크가 가득 차 있을 때, 파일이 찾는 디렉토리에 없을 때, 이름이 잘못 되었을 때, 접근이 제한되어 있을 때, 하드웨어에 문제가 있을 때 실패할 수 있다.
getc(), putc()함수는 getchar(), putchar() 함수와 아주 비슷하게 작동한다. 차이는, getc()와 putc()는 어느파일을 사용할 것인지 알려주어야 한다는 것이다.
ch=getc(fp) -> "fp가 나타내는 파일로부터 하나의 문자를 얻는다."
putc(ch,fpout) -> "문자 ch를 FIE포인터 fpout이 나타내는 파일에 출력한다."
putc()전달인자 리스트에서, 문자가 앞에오고 파일 포인터가 뒤에온다.
stdout은 stdio.h에 표준출력에 연결되는 파일 포인터로 정의되어 있다. 그러므로 putc(ch,stdout)은 putchar(ch)와 동일한 효과를 낸다. 실제로 putchar()는 일반적으로 putc()로 정의되어 있다.
파일로 부터 데이터를 읽는 프로그램은, 파일의 끝에 도달했을 때 읽기를 멈출 필요가 있다. getc()함수는, 하나의 문자를 읽으려 시도하다가 파일의 끝에 도달했다는 것을 발견하면, EOF라는 특별한 값을 리턴한다. 그래서 C프로그램은 파일의 끝을 읽은 후에 파일의 끝에 도달했다는 것을 알게 된다.
비어있는 파일을 읽으려 시도하는 문제를 피하려면, 진입조건 루프를 파일 입력에 사용해야 한다. getc()(그리고 다른 C의 입력 함수들)의 그와 같은 설계 특성때문에, 프로그램은 루프 몸체에 들어가기전 최초의 읽기를 시도해야한다.
ex)
int ch;
FILE *fp;
fp = fopen("abc.txt","r");
while((ch = getc(fp))!=EOF) //루프 들어가기전 진입조건
{
putchar(ch); //입력 처리
}
fclose(fp)함수는, 필요할 때 버퍼를 비우면서 fp가 가리키는 파일을 닫는다. fclose()함수는 파일을 성공적으로 닫았으면 0을 리턴하고, 그렇지 않으면 EOF를 리턴한다. fclose 함수는, 디스크가 가득 차있을 때, 플로피 디스크가 제거 되었을 때, 또다른 에러가 발생했을 때 파일을 닫는데 실패할 수 있다.
표준 파일을 가리키는 포인터
stdio.h 파일은, 세개의 파일 포인터를 C프로그램이 자동으로 여는 세개의 표준 파일에 연결한다.
표준파일 파일포인터 일반적으로
-------------------------------------------------------------------------------------------------------------------------
표준 입력 stdin 키보드 표준 출력 stdout 스크린 표준 에러 stderr 스크린
위 포인터들은 모두 FILE을 가리키는 포인터형이다. 그래서 이들을 표준 입출력 함수들의 전달인자로 사용할 수 있다.
파일 입출력 함수들에게 어느 파일을 가지고 작업할 것인지 알려주기 위해 파일 포인터를 사용할 필요가 있다. getc()와 putc()와 마찬가지로 파일 입출력 함수들은 stdout과 같은, FILE을 가리키는 포인터를 지정할 것을 요구하거나, fopen()의 리턴값을 사용할 것을 요구한다.
동시에 열어 놓을수 있는 파일들의 개수에는 제한이 있다. 그 제한은 사용하는 시스템과 컴파일러에 따라 다르다. 일반적으로 그 제한은 10 ~ 20 개 이다. 또한, 파일들을 동시에 열어 놓지 않는다면, 서로 다른 파일에 같은 파일 포인터를 사용할 수 있다.
rewind()함수는 파일 포인터를 파일의 시작으로 옮긴다. rewind()는 파일 포인터를 전달인자로 사용한다.
fprintf()와 fscanf()함수는 FILE 포인터를 마지막 전달인자가 아니라 첫번째 전달인자로 사용한다.
fgets()함수는 세개의 전달인자를 사용한다. 첫번째 전달인자는, gets()와 마찬가지로, 입력을 저장할 주소(type char *)이다. 두번째 전달인자는, 입력 문자열의 최대 크기를 나타내는 정수이다. 마지막 세번째 전달인자는, 읽을 파일을 가리키는 파일 포인터다.
fgets(buf,STLEN,fp);
fgets()함수는 최대 문자열 크기보다 적은 수의 문자들을 읽을 때까지 또는 파일끝을 만날때까지 문자들을 읽되, 첫 개행 문자까지 읽는다. 그리고 fgets()는 읽은 것이 문자열이 되도록 종결 널문자를 추가한다. fgets()함수는 EOF를 만나면 NULL값을 반환한다. 이 값을 사용하여 파일의 끝에 도달했는지 검사할 수 있다.
fputs()함수는 두개의 전달인자를 사용한다. 첫번째 전달인자는 문자열의 주소다. 두번째 전달인자는 파일 포인터이다. 이함수는 그 주소에 들어있는 문자열을 파일포인터가 가리키는 파일에 기록한다.
fputs(buf,fp);
fgets()는 개행 문자를 유지하고, fputs()는 개행 문자를 덧붙이지 않기 때문에, 이들은 서로 협력하여 잘 동작한다.
fseek() 함수는, 파일을 마치 배열처럼 다룰 수 있게 한다. 이 함수는 fopen()에 의해 열려진 파일에 들어있는 특정 바이트로 직접 이동할 수 있게 해준다.
fseek()는 세개의 전달인자를 사용한다. 첫번째 전달인자는, 처리할 파일을 가리키는 FILE 포인터이다. 이 파일은 fopen()함수에 의해 미리 열려 있어야 한다. fseek()의 두번째 전달인자는 오프셋(offset)이라고 한다. 이 전달인자는 시작 위치로 부터 얼마나 멀리 가야하는지 알려준다. 이 전달인자는 long형 값이어야 한다. 이 값은 양수(앞으로), 음수(뒤로), 0(현재 위치에 머무른다)이 될 수 있다.
세번째 전달인자는 모드(mode)이다. 그것은 시작 위치를 나타낸다. ANSI에서, 모드와 관련된 명단 상수(manifast contant)들은 stdio.h헤더 파일에 정의되어 있다.
모드 시작위치
SEEK_SET 파일의 시작
SEEK_CUR 현재 시작
SEEK_END 파일의 끝
다음은 fseek() 함수 호출의 몇가지 예이다.
fseek(fp,0L,SEEK_SET); //파일의 시작으로 간다.
fseek(fp,10L,SEEK_SET); //파일의 시작에서 10바이트 앞으로 간다.
fseek(fp,2L,SEEK_CUR); //현재 위치에서 2바이트 앞으로 간다.
fseek(fp,0L,SEEK_END); //파일의 끝으로 간다.
fseek(fp,-10L,SEEK_END); //파일의 끝에서 10바이트 뒤로간다.
모든것이 성공적이면 fseek()은 0을 리턴한다. 파일의 경계를 벗어나려는 시도와 같은 에러가 있으면 fseek()은 -1을 리턴한다.
ftell()함수는 long형이다. 이 함수는 현재의 파일 위치를 리턴한다. ANSI C에서, 이 함수는 stdio.h에 선언되어 있다. ftell()은 파일의 시작으로 부터 첫바이트를 0으로 하는 바이트 수를 리턴함으로써 적용된다. 그러나 텍스트 모드로 열린 파일들에 반드시 적용되는 것은 아니다.
텍스트 모드에서 제대로 동작하는 fseek() 호출
함수 호출 효과
fseek(file,0L,SEEK_SET) 파일의 시작으로 간다.
fseek(file,0L,SEEK_CUR) 현재 위치에 머무른다.
fssek(file,0L,SEEK_END) 파일의 끝으로 간다.
fseek(file,ftell-pos,SEEK_SET) 파일의 시작에서 ftell-pos 만큼 떨어진 위치로 간다. ftell-pos는 ftell()이 리턴하는 값이다.
fseek()와 ftell()함수들은 파일 크기를 long형으로 나타낼 수 있는 값으로 제한한다. ANSI C는 커다란 파일을 처리하도록 설계된 두개의 새로운 위치 지정 함수를 도입했다. long형 값으로 위치를 지정하는 대신에, 그 목적을 위해 설계된(파일 위치 데이터형을 의미하는) fpos_t라는 새로운 데이터형을 사용한다.
ANSI C는 fpos_t를 사용하는 방법을 정의한다. fgetpos()함수는 다음과 같은 프로토 타입을 가진다.
int fgetpos(FILE * restrict stream, fpos_t * restrict pos);
호출되었을 때, 이 함수는 fpos_t값을 pos가 가리키는 위치에 넣는다. 그 값은 파일 안에서의 어떤 위치를 나타낸다. 이 함수는 성공하면 0을 리턴하고, 실패하면 0이아닌 값을 리턴한다.
일반적으로, 표준 입출력을 사용하는 첫 단계는 fopen()을 사용하여 파일을 여는 것이다.(그러나 stdin, stdout, stderr 파일들은 자동으로 열린다는 것을 기억하라.) fopen()함수는 파일을 열 뿐만 아니라 버퍼(읽기- 쓰기 모드를 위한 두개의 버퍼)도 설정한다. 또한 이 함수는 파일과 버퍼에 관한 데이터가 들어있는 데이터 구조체를 설정한다. 그리고 다른 함수들이 그 구조체를 어디에서 찾아야 하는지 알 수 있도록 그 구조체를 가리키는 포인터를 리턴한다. 이 값이 fp라는 이름의 포인터변수에 대입된다고 가정하자. 우리는 이것을 fopen() 함수가 "하나의 스트림을 열었다"라고 말한다. 파일을 텍스트모드로 열면, 텍스트 스트림을 얻는다. 파일을 바이너리 모드로 열면 바이너리 스트림을 얻는다.
일반적으로 그 데이터 구조체는 스트림의 현재 위치를 나타내는 파일 위치 표시자를 가지고 있다. 또한 그 구조체는 에러와 파일끝을 나타내는 지시자, 버퍼의 시작을 가리키는 포인터, 파일 식별자, 버퍼 안으로 실제로 복사된 바이트 수에 해당하는 카운트도 가지고 있다.