C의 입출력 라이브러리는 표준 헤더 중에서 가장 크고 가장 중요한 부분이다. 그런 위상에 걸맞게 이 헤더를 다루는데 한 챕터 전체를 할애한다(그리고 책에서 가장 긴 챕터다).
개요
21.1 stream의 개념, FILE type, input and output redirection, difference between text files and binary files
22.2 파일과 함께 사용되도록 디자인된 함수들, 파일을 열고 닫는 파일들을 포함해서
22.3 printf, scanf 그리고 관련된 "formatted input/output" 함수들
그리고 unformatted 함수들(22.4, 22.5)
22.4 한번에 문자 한 개를 읽고 쓰는 getc, putc, 관련 함수들
22.5 한번에 한 줄을 읽고 쓰는 gets, puts, 관련 함수들
22.6 데이터의 blocks를 읽고 쓰는 fread, fwrite
22.7 파일에 random access operation을 수행하는 방법
22.8 printf와 scanf의 변종인 sprintf, snprintf, sscanf(문자열을 읽거나 문자열에 쓰는 함수들)
이 챕터에서는 <stdio.h> 헤더 중 8개의 함수를 제외하고 모두 다룬다. 그중 하나인 perror 함수는 <errno.h> 헤더와 밀접한 관련이 있으므로 그와 함께 24.2에서 다룬다. 26.1에서는 나머지 함수들(vfprintf, vprintf, vsprintf, vsnprintf, vfscanf, vscanf, vsscanf)를 다룬다.
C89에서는 모든 표준 입출력 함수가 <stdio.h>에 들어 있었지만, C99에서는 그렇지는 않다. 몇몇 입출력 함수는 <wchar.h> 헤더에 선언되어 있다. <wchar.h>의 함수들은 보통 문자가 아닌 와이드 문자를 다루며, <stdio.h>에 나오는 함수들과 가까운 닮은 함수들이다. <stdio.h>의 데이터를 읽고 쓰는 함수들은 byte input/output functions라고 불리고, <wchar.h>에 있는 유사한 함수들은 wide-character input/output functions라고 불린다.
22.1 Streams
C에서 stream이라는 용어는 입력의 모든 원천 또는 출력의 모든 목적지를 의미한다. 많은 작은 프로그램들에서는 입력을 하나의 스트림(보통 키보드와 관련된 것)으로 받아서 하나의 스트림(보통 화면과 관련된 것)으로 출력한다.
더 큰 프로그램들은 추가적인 스트림을 필요로 하기도 한다. 이런 스트림들은 다양한 미디어(HDD, CD, DVD, 플래시 메모리 등)에 저장된 파일이기도 하고, 파일을 저장하지 않는 장치들(네트워크 포트, 프린터 등)과 관련된 것일수도 있다. 우리는 흔하고 이해하기 쉬운 파일에 집중한다. stream을 사용할 곳에 file이란 용어를 쓰기도 할 것이다. <stdio.h>에 있는 함수들은 파일 뿐만 아니라 모든 스트림에 대해 잘 작동한다는 것을 유념하고 넘어가자.
File Pointers
스트림에 접근하는 것은 file pointer를 통해 이뤄진다. 그 타입은 FILE *이며, <stdio.h>에 선언되어 있다. 어떤 스트림은 표준 이름을 갖는 파일 포인터로 표현된다. 추가적인 파일 포인터들도 선언할 수 있다. 예를 들어, 프로그램에서 표준 포인터에 추가해 두개의 스트림을 필요로 하면, 다음과 같은 선언을 할 수 있다.
FILE *fp1, *fp2;
프로그램에서는 FILE * 변수들을 제한 없이 만들 수 있지만, 보통 운영체제에서 한번에 열릴 수 있는 스트림의 갯수를 제한한다.
Standard Streams and Redirection
<stdio.h>는 표준 스트림을 제공한다. 이 스트림들은 준비되었고 사용하기만 하면 된다. 우리는 이것들을 선언하지도 않고, 열거나 닫지도 않는다.
지금까지 우리가 사용해온 함수들 - printf, scanf, putchar, getchar, puts, gets - 는 입력을 stdin에서 얻어서 출력을 stdout으로 보낸다. 기본값으로, stdin은 키보드를 나타낸다. stdout과 stderr는 화면을 나타낸다. 하지만 많은 운영 체제에서는 redirection이라는 메커니즘을 통해 이 기본적인 의미를 수정할 수 있게 허용한다.
통상적으로 프로그램이 입력을 키보드가 아닌 파일에서 받을 수 있도록 프로그램에 강제할 수 있다. command line에 "<파일명"을 입력하면 된다.
demo <in.dat
이 방법은 input redirection이라고 알려져 있으며 stdin 스트림이 키보드가 아닌 파일(이 경우 in.dat)을 나타내도록 본질적으로 만들어 준다. 이 redirection의 멋진 점은 demo 프로그램에서는 자신이 in.dat에서 읽어온다는 점을 모른다는 것이다. 단지 어떤 데이터를 stdin에서 얻어오는 것은 키보드를 통해 온다고 인식한다.
Output redirection도 유사하다. stdout 스트림을 redirect하는 것은 ">파일명"이라고 command line에 입력하면 된다.
demo >out.dat
stdout에 쓰인 모든 데이터는 이제 화면에 나타나는 대신 out.dat 파일로 들어가게 된다. 다음과 같이 input/output redirection을 동시에 사용할 수도 있다.
demo <in.dat >out.dat
// < > 문자는 파일명과 붙어있지 않아도 되고, redirection의 순서가 바뀌어도 괜찮다.
demo < in.dat > out.dat
demo >out.dat <in.dat
output redirection의 한가지 문제는, stdout에 쓰이는 모든 것들이 파일에 들어간다는 것이다. 만약 프로그램에서 오류가 발생해서 에러 메시지를 쓰기 시작한다면, 파일을 들여다보기 전까지는 그것을 보지 못한다. 그래서 stderr가 생겼다. 에러 메시지는 stdout이 아닌 stderr에 따로 씀으로써, stdout이 redirect되었을 때에도 우리는 에러 메시지를 화면을 통해 볼수 있게 된다. (운영체제에서는 가끔 stderr의 redirection도 허용하기도 한다.)
Text Files versus Binary Files
<stdio.h> 는 두가지 종류의 파일을 지원한다. 텍스트와 바이너리이다. text file의 바이트들은 문자를 나타내며, 사람이 파일을 보거나 수정할 수 있게 해 준다. C 프로그램의 소스 코드는 text file로 저장되어 있다.
반면 binary file은 바이트가 꼭 문자로 저장되지 않는다. 바이트들의 묶음은 정수나 부동 소수점 숫자 같이 데이터의 다른 형식을 나타낼 수도 있다. 실행 가능한 C 프로그램은 binary file로 저장된다.
텍스트 파일은 바이너리 파일엔 없는 두 가지 특징을 가지고 있다.
1. 텍스트 파일은 라인으로 구분된다. 텍스트 파일의 각각의 라인은 한개, 또는 두개의 특별한 문자로 끝난다. 그 문자를 선택하는 것은 운영체제가 한다. 윈도우즈에서는, end-of-line 마커가 carriage-return 문자('\x0d')와 바로 뒤에 오는 line-feed 문자('\x0a')이다. UNIX와 신버전의 매킨토시 운영 체제(Mac OS)의 end-of-line 마커는 하나의 line-feed 문자이다. Mac OS의 구버전들은 하나의 carriage-return 문자를 사용한다.
2. 텍스트 파일은 특별한 "end-of-file" 마커를 포함할 수 있다. 어떤 운영체제는 특별한 바이트가 텍스트 파일의 끝을 나타내는 마커로 사용되는 것을 허용한다. 윈도우즈에서 그 마커는 '\x1a' (Ctrl+Z)이다. Ctrl-Z가 존재해야 한다는 요구사항은 없지만, 만약 존재한다면 그것은 파일의 끝을 나타내어야 한다. Ctrl-Z 뒤의 모든 바이트들은 무시된다. UNIX를 포함한 대부분의 다른 운영 체제는 end-of-file을 나타내는 특별한 문자가 존재하지 않는다.
바이너리 파일은 라인으로 구분되지 않는다. 바이너리 파일엔 end-of-line, end-of-file 마커가 없으며 모든 바이트가 동등하게 간주된다.
파일을 저장할 때, 우리는 텍스트 형태로 저장할 지, 바이너리 형태로 저장할 지를 결정해야 한다. 숫자 32767을 파일에 저장한다고 해 보자. 하나는 텍스트 형태로, 문자 '3', '2', '7', '6', '7'을 저장하는 것이다. 문자 집합이 ASCII인 경우, 다음과 같이 5바이트가 된다.
다른 옵션은 바이너리 형태로 저장하는 것이다. 이 때에는 2바이트가 필요하다.
(리틀 엔디안 방식으로 저장되는 운영 체제의 경우는 다음과 같이 바이트의 순서가 뒤바뀐다)
위 사례에서 볼 수 있듯이 바이너리 형태로 숫자들을 저장하는 것은 공간을 꽤 절약해 준다.
파일로부터 읽거나 파일에 쓰는 프로그램을 작성할 때, 우리는 그것이 텍스트 파일인지 바이너리 파일인지를 고려해야 한다. 파일의 내용을 화면에 출력하는 프로그램은 보통 그것을 텍스트 파일이라고 가정할 것이다. 반면 파일을 복사하는 프로그램은, 복사될 파일이 텍스트 파일이라고 가정할 수 없다. 만약 그렇게 한다면 end-of-file 문자가 들어있는 binary 파일은 완벽하게 복사되지 않게 된다. 만약 파일이 텍스트 파일인지 바이너리 파일인지 모른다면, 바이너리라고 가정하는 것이 더 안전하다.
22.2 File Operations
input/output redirection의 매력은 단순함이다. 파일을 열고 닫거나 기타 파일 조작에 대한 명시적인 것들을 수행할 필요가 없다. 그러나 이것은 대부분의 프로그램들에게는 너무 제한적이다. 프로그램이 redirection에 의존하면 파일에 대한 조작을 할 수 없다. 심지어 파일명도 알 수 없다. 또한 프로그램이 두개 이상의 파일에서 읽거나 쓰는 경우 도움이 되지 못한다.
redirection이 충분하지 못한 경우, 우리는 <stdio.h>에서 제공하는 파일 작업을 사용하게 된다. 이 섹션에서는 그러한 작업들, 파일 열기, 파일 닫기, 파일 버퍼 방식 변경, 파일 삭제, 파일 이름 바꾸기 등을 살펴본다.
Opening a File
File *fopen(const char * restrict filename,
const char * restrict mode);
스트림으로 사용하기 위해 파일을 열기 위해서는 fopen 함수를 호출해야 한다. fopen의 첫번째 인자는 열 파일의 이름이 담긴 문자열이다. ("파일 이름"이란 것은 드라이버 지정자와 경로가 포함된 파일의 위치에 대한 정보를 포함할 수도 있다) 두 번째 인자는 "mode string"으로 파일에 대해 어떤 작업을 수행할 지를 결정한다. 예를 들어 "r"은 데이터를 파일에서 읽어오기는 하지만 파일에 쓰지는 않는다는 것을 나타낸다.
fopen 함수의 원형에 restrict 키워드가 두번 나타난다. 이것은 filename과 mode는 같은 메모리 영역을 공유하는 문자열을 가리켜서는 안된다는 것을 나타낸다. C89에서의 원형은 restrict 키워드가 제외되어 있다는 점을 빼면 동일하다.
윈도우에서 filename을 쓸 때 \문자는 escape sequence의 시작을 나타낸다는 점을 유의해야 한다.
fopen("c:\project\test1.dat", "r")
이라고 쓰면 \t가 tab character를 나타내고 프로그램은 이를 인식하지 못한다. \p는 유효한 escape sequence가 아니고 그 의미는 정의되어 있지 않다.
이 문제를 해결하는 방법은 두 가지가 있다. 하나는 \ 대신 \\를 쓰는 방법이다.
fopen("c:\\project\\test1.dat", "r")
다른 방법은 \ 문자가 아니라 /를 쓰는 것이다.
fopen("c:/project/test1.dat", "r")
윈도우에서는 \ 대신 /를 썼을 때 디렉토리 구분자로 인식할 것이다.
fopen 함수는 변수에 저장해서 원할 때마다 사용할 수 있는 파일 포인터를 리턴한다. 다음은 fopen 호출의 전형적인 예이다. fp의 형식은 FILE * 이다.
fp = open("in.dat", "r"); /* opens in.dat for reading */
나중에 프로그램에서 in.dat로부터 읽는 입력 함수를 호출할 때, fp를 인자로 전달하게 된다.
만약 파일을 열지 못한다면, fopen은 NULL 포인터를 리턴한다. 파일이 존재하지 않거나, 다른 장소에 있거나, 권한이 없는 경우이다. fopen이 파일을 항상 열거라는 가정을 해선 안된다. 언제나 fopen의 리턴 값이 null 포인터가 아닌지를 확인해야 한다.
Modes
fopen에 전달할 mode 문자열은 파일에 수행할 작업 뿐 아니라 파일이 텍스트 파일인지 바이너리 파일인지에 대한 정보도 담고 있다.
텍스트 파일의 모드 문자열
바이너리 파일의 모드 문자열
fopen으로 바이너리 파일을 열 때는, 글자 b를 추가한다. <stdio.h>는 데이터를 쓰는 것과 데이터를 추가하는 것을 구분한다. 데이터를 파일에 쓸 때는, 보통 기존에 있던 곳에 데이터를 덮어쓴다. appending(추가)을 위해 파일을 열었을 때는, 데이터는 파일의 끝에 더해지고, 기존에 있던 파일 내용이 보존된다.
읽기와 쓰기 모두를 위해 파일을 열었을 때는(mode 문자열에 + 문자를 넣어서) 특별한 규칙이 적용된다. 읽기 작업에서 파일의 끝에 도달하지 않는 한 먼저 file-positioning 함수를 호출하지 않고서는 읽기에서 쓰기로 전환할 수 없다. 또한, fflush나 file-positioning 함수를 호출하지 않으면 쓰기에서 읽기로 전환할 수 없다.
Closing a File
int fclose(FILE *stream);
fclose 함수는 더 이상 사용하지 않는 파일을 프로그램이 닫을 수 있도록 해 준다. fclose의 인자는 반드시 fopen이나 freopen에서 얻은 파일 포인터여야만 한다. 파일이 성공적으로 닫혔다면, fclose는 0을 반환한다. 그렇지 않은 경우에는 에러 코드 EOF(<stdio.h>에 정의된 매크로)를 반환한다.
Attaching a File to an Open Stream
FILE *freopen(const char * restrict filename,
const char * restrict mode,
FILE * restrict stream);
freopen은 열려 있는 스트림에 다른 파일을 붙인다. 가장 흔하게 freopen을 사용하는 것은 표준 스트림(stdin, stdout, stderr) 중 하나와 파일을 연관시키는 것이다. 예를 들어 파일 foo에 프로그램이 쓰도록 하게 하려면, 다음과 같이 freopen을 호출한다.
if (freopen("foo", "w", stdout) == NULL) {
/* error; foo can't be opend */
}
stdout과 기존에 연관되어 있던 파일을 닫으면(command line redirection이나 freopen을 앞서서 호출한 경우), freopen은 foo를 열어서 stdout과 연관시킨다.
freopen의 정상적인 반환값은 자신의 세번째 인자(파일 포인터)이다. 만약 새 파일을 열지 못한 경우, null 포인터를 리턴한다. (freopen은 예전 파일이 닫히지 못하는 오류는 무시한다.)
C99에서 새롭게 추가된 것으로, 만약 filename이 null 포인터인 경우, freopen은 현재 스트림의 모드를 지정된 모드 파라미터로 변경하려고 시도한다. 이 기능을 반드시 구현할 것을 요구하지는 않지만, 만약 구현하는 경우에는 특정 모드로의 변경을 막는 제한을 설정할 수 있다.
Obtaining File Names from the Command Line
파일을 여는 프로그램을 작성할 때 어떻게 파일의 이름을 프로그램에 전달해야 할까? 파일 이름 자체를 프로그램 내에 집어넣는 것은 유연성이 떨어지고, 사용자가 파일명을 입력하게 만드는 것은 다소 어색하다. 가장 좋은 해결책은 프로그램이 파일 이름을 command line에서 받아오는 것이다. 예를 들어 demo라는 프로그램을 실행할 때, command line에 파일 명을 같이 입력해서 프로그램에게 전달할 수 있다.
demo names.dat dates.dat
13.7에서 main 함수를 두 개의 매개변수를 갖는 함수로 선언해서 command-line 인자에 접근하는 방법을 다룬 바 있다.
/*
canopen.c
This program determines if a file exists and can be opened for reading.
When the program is run, the user will give it a file name to check:
canopen file
The program will then print either file can be opened or file can't be
opened. If the user enters the wrong number of arguments on the command
line, the program will print the message "usage: canopen filename" to
remind the user that canopen requires a single file name.
We can use redirection to discard the output of canopen and simply test
the status value it returns.
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
if (argc != 2) {
printf("usage: canopen filename\n");
exit(EXIT_FAILURE);
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("%s can't be opened\n", argv[1]);
exit(EXIT_FAILURE);
}
printf("%s can be opened\n", argv[1]);
fclose(fp);
return 0;
}
Temporary Files
FILE *tmpfile(void);
char *tmpnam(char *s);
실제 사용되는 프로그램들은 종종 임시 파일 - 프로그램이 실행되는 동안에만 존재하는 파일들 - 을 만들어야 할 때가 있다. 예를 들어 C 컴파일러도 종종 임시 파일들을 생성한다. 컴파일러는 먼저 C 프로그램을 어떤 중간 형태로 번역해서 파일에 저장한다. 컴파일러는 나중에 파일을 읽어서 object code로 번역한다. 프로그램이 완전히 컴파일되면, 중간 형태를 담은 파일은 더 이상 보존될 필요성이 사라진다. <stdio.h>에서는 임시 파일들의 작업을 위해 tmpfile과 tmpnam, 두 가지 함수를 제공한다.
tmpfile은 임시 파일을 생성한다("wb+" 모드로 열린다). 그 파일은 닫히거나 프로그램이 종료될 때까지 존재한다. tmpfile의 호출은 임시 파일에 접근할 수 있는 파일 포인터를 반환한다.
FILE *tempptr;
...
tempptr = tempfile(); /* creates a temporary file */
만약 파일 생성에 실패하면, tmpfile은 null 포인터를 리턴한다.
tmpfile은 사용하기 쉽지만, 두 가지 문제점이 있다. 1) tempfile이 생성하는 파일명을 알 수 없다. 2) 나중에 그 파일을 영구적으로 저장할 수 없다. 만약 이런 제약이 문제가 된다면, 대안은 fopen으로 임시 파일을 생성하는 것이다. 물론 기존에 생성한 이름과 같은 파일명을 갖길 원하지 않기 때문에, 새로운 파일명을 생성할 방법이 필요하다. 그래서 tmpnam 함수가 생겨났다.
tmpnam은 임시 파일을 위한 이름을 생성한다. 만약 인자가 null 포인터인 경우에는, tmpnam은 파일명을 static variable에 저장하고 그에 대한 포인터를 반환한다. 그렇지 않은 경우에는, 전달받은 문자 배열에 파일명을 복사한다.
char *filename;
...
filename = tmpnam(NULL); /* creates a temporary file name */
char filename[L_tmpnam];
...
tmpnam(filename); /* creates a temporary file name */
후자의 경우, tmpnam은 배열의 첫번째 문자를 가리키는 포인터도 리턴한다. L_tmpnam은 <stdio.h>에 정의된 매크로로, 임시 파일 이름을 저장할 문자 배열의 길이를 지정한다.
tmpnam의 인자가 최소 L_tmpnam 만큼의 길이를 갖는 배열을 가리켜야 한다. 또, tmpnam을 너무 자주 호출하지 않도록 주의해야 한다. TMP_MAX 매크로(<stdio.h>에 정의)는 프로그램 실행 중에 tmpnam이 만들 수 있는 최대의 임시 파일 이름의 갯수를 지정한다. 만약 파일명 생성에 실패하면, tmpnam은 null 포인터를 반환한다.
File Buffering
int fflush(FILE *stream);
void setbuf(FILE * restrict stream,
char * restrict buf);
int setvbuf(FILE * restrict stream,
char * restrict buf,
int mode, size_t size);
디스크 드라이브로부터/로 데이터를 전송하는 것은 상대적으로 느린 작업이다. 그 결과, 프로그램에서 파일에서 바이트를 읽거나 파일에 바이트를 쓸 때마다 디스크 파일에 직접 접근하는 것은 실현할 수 없다. 납득할만한 퍼포먼스를 해낼 수 있는 비결은 buffering이다. stream에 쓰인 데이터는 사실은 메모리의 버퍼 영역에 저장된다. 버퍼가 꽉 차면(또는 스트림이 닫히면), 버퍼가 "flushed" (실제의 출력 장치에 쓰인다). 입력 스트림도 비슷한 방식으로 버퍼될 수 있다. 버퍼는 입력 장치에서 온 데이터를 담고 있다. 입력은 장치 자체가 아니라 버퍼로부터 읽는다. 버퍼링은 효율성에서 막대한 이득을 가져온다. 버퍼로부터 바이트를 읽거나 버퍼에 바이트를 저장하는 것은 거의 시간이 소요되지 않기 때문이다. 물론 버퍼의 내용을 디스크와 전송하는 데는 시간이 소요되지만, 큰 "block move"는 미미한 여러개의 바이트를 이동시키는 데 비하면 훨씬 시간이 적게 걸린다.
<stdio.h>의 함수들은 이롭다고 생각될 때 자동적으로 버퍼링을 수행한다. 버퍼링은 보통 보이지 않는 곳에서 이뤄지고 우리가 신경쓰지 않아도 된다. 하지만 가끔씩 적극적으로 버퍼링에 개입해야 할 때가 있다. 그 때 우리는 fflush, setbuf, setvbuf 함수를 사용한다.
프로그램이 파일을 출력에 쓸 때, 데이터는 보통 버퍼에 먼저 들어간다. 버퍼는 꽉 차거나 파일이 닫힐 때 자동적으로 flush된다. 하지만 fflush를 호출하면, 프로그램은 파일의 버퍼를 바로 flush한다.
fflush(fp); /* flushes buffer for fp */
는 fp와 연결된 파일의 버퍼를 flush한다.
fflush(NULL); /* flushes all buffers */
는 모든 출력 스트림을 flush한다. fflush는 성공적인 경우 0을, 오류가 발생한 경우 EOF를 리턴한다.
C 표준에 따르면, fflush호출은 다음에 대해 정의되어 있다: 스트림이 (a) 출력에 대해 열려 있는 경우. 또는 (b) 업데이트로 열려 있고(fopen mode에 "+"가 붙은 경우) 마지막 작업이 읽기가 아닌 경우. 그 외 다른 모든 경우 fflush의 호출시 효과는 정의되어 있지 않다. 만약 fflush가 null 포인터를 전달받으면 (a)나 (b)를 만족하는 모든 스트림을 flush한다.
setvbuf는 스트림이 버퍼되는 방식을 변경하거나 버퍼의 크기와 위치를 제어할 수 있게 해 준다. 함수의 세번째 인자는 버퍼의 종류를 지정하며, 다음 세 가지 매크로 중 하나만 지정할 수 있다.
_IOFBF(full buffering). 버퍼가 비어있을 때 스트림에서 데이터를 읽어들이고 버퍼가 꽉 찼을 때 스트림에 데이터를 쓴다.
_IOLBF(line buffering). 한번에 한 줄(line)씩 데이터를 스트림에서 읽거나 스트림에 쓴다.
_IONBF(no buffering). 버퍼를 사용하지 않고 데이터를 스트림에서 바로 읽고 스트림에 바로 쓴다.
세 매크로는 <stdio.h>에 정의되어 있다. interactive 장치가 아닌 경우 full buffering이 기본값이다.
setvbuf의 두번째 인자(null 포인터가 아닌 경우)는 원하는 버퍼의 주소이다. 버퍼는 static storage duration을 갖거나, automatic storage duration을 갖거나, 심지어 동적할당될 수도 있다. 버퍼를 automatic으로 만들면 block이 끝날 때 그 공간은 자동적으로 해제된다. 버퍼를 동적할당하는 경우에는 우리가 직접 버퍼를 해제할 수 있게 된다. 마지막 인자는 버퍼에 있는 바이트의 숫자이다. 큰 버퍼는 더 나은 퍼포먼스를 보일 수 있고, 작은 버퍼는 공간을 절약한다.
예를 들어, 다음 setvbuf 호출은 stream의 버퍼링을 full buffering으로, buffer 배열을 버퍼로, N 바이트를 사용한다.
char buffer[N];
...
setvbuf(stream, buffer, _IOFBF, N);
setvbuf는 반드시 stream이 오픈된 이후에, 그리고 모든 다른 작업이 그에 대해 수행되기 이전에 호출되어야 한다.
setvbuf의 두번째 인자를 null 포인터로 호출할 수도 있다. 이는 setvbuf가 지정된 크기의 버퍼를 만들 것을 요청한다. 그것이 성공적이었다면 setvbuf는 0을 리턴한다. 만약 mode 인자가 부적합하거나 요청이 실행되지 못했다면 0이 아닌 값을 리턴한다.
setbuf는 더 오래된 함수로 버퍼링 모드와 사이즈에 기본값을 가정한다. buf가 null 포인터인 경우, setbuf(stream, buf) 호출은
(void) setvbuf(stream, NULL, _IONBF, 0);
과 동일하다.
buff가 null 포인터가 아닌 경우, 다음과 동일하다.
(void) setvbuf(stream, buf, _IOFBF, BUFSIZ);
BUFSIZ는 <stdio.h>에 정의된 매크로다. setbuf는 구식으로 간주되며 새로운 프로그램에서 사용하는 것은 추천하지 않는다.
setvbuf나 setbuf를 사용할 때는, 버퍼가 할당 해제되기 전에 스트림을 닫아야 한다. 특히 버퍼가 함수에 local이고 automatic storage duration을 갖는 경우, 함수가 값을 리턴하기 전에 스트림을 닫아야 한다.
Miscellaneous File Operations
int remove(const char *filename);
int rename(const char *old, const char *new);
remove와 rename 함수는 프로그램에서 기본적인 파일 관리 작업을 할 수 있게 해 준다. 이 장의 다른 함수들과 달리, remove와 rename 함수는 파일 포인터가 아닌 파일 이름을 넣어 사용한다. 두 함수 모두 성공하면 0을, 실패하면 0이 아닌 값을 리턴한다.
remove는 파일을 삭제한다.
remove("foo"); /* deletes the file named "foo" */
만약 tmpfile이 아닌 fopen으로 임시 파일을 생성한 경우, 프로그램 종료 전에 remove로 그 임시 파일을 삭제할 수 있다. 파일을 삭제하기 전에 스트림이 닫혀야 한다. 열려 있는 파일을 삭제하는 경우의 효과는 implementation-defined.
rename은 파일의 이름을 바꾼다.
rename("foo", "bar"); /* renames "foo" to "bar" */
rename은 fopen으로 생성한 임시 파일을, 프로그램 종료 전에 영구적인 파일로 보존해야겠다고 결정했을 때 유용하다. 만약 새로운 파일 이름이 이미 존재하는 경우의 효과는 implementation-defined.
만약 이름을 바꾸려는 파일이 열려 있는 경우, rename을 호출하기 전에 그것을 닫아야 한다. 열려 있는 파일의 이름을 바꾸려고 하면 실패할 수도 있다.