C 파일 입출력
1. 개요
C 파일 입출력은 C 표준 라이브러리의
| {"caption":"\"C 언어의 표준 입출력은 stdio.h 헤더 파일에 정의되어 있다.\""} | |
| 정의 위치 | stdio.h 헤더 파일 |
|---|---|
| 표준 스트림 | stdin stdout stderr |
|---|---|
| 파일 스트림 | 파일 포인터를 사용하여 파일에 접근 |
| 파일 접근 | fopen fclose |
|---|---|
| 문자 입출력 | fgetc fgets fputc fputs getc getchar putc putchar ungetc |
| 직접 입출력 | fread fwrite |
| 위치 지정 | fgetpos fseek fsetpos ftell rewind |
| 포맷 입출력 | fprintf fscanf printf scanf snprintf sprintf sscanf vfprintf vfscanf vprintf vscanf vsnprintf vsprintf vsscanf |
| 에러 처리 | clearerr feof ferror perror |
| 관련 주제 | C 파일 입출력 C 문자열 처리 C 언어의 동적 메모리 할당 |
|---|
-
입출력 -
터치 타이핑
터치 타이핑은 키보드를 보지 않고 손가락 위치를 기억해 타이핑하는 기술로, 효율적인 방법으로 알려져 있으며 꾸준한 연습으로 속도와 정확성을 향상시킬 수 있다. -
입출력 -
입력
입력은 컴퓨터에 정보나 명령을 전달하는 방식으로, 키보드, 마우스, 스캐너, 음성 입력 장치, 게임 컨트롤러 등 다양한 장치를 통해 이루어지며 '가져오다' 또는 '읽어 들이다'와 같은 의미로도 사용된다. -
C 표준 라이브러리 -
파일 끝
파일 끝(EOF)은 데이터 소스에서 더 이상 읽을 데이터가 없음을 나타내는 조건이다. -
C 표준 라이브러리 -
C 자료형
C 자료형은 C 언어에서 데이터 형태를 정의하는 기본 요소로, 다양한 크기와 속성을 가진 산술, 부울 자료형과 구조체, 공용체 등을 제공하며, 컴파일러 및 시스템 아키텍처에 따라 크기와 동작 방식이 달라질 수 있다.
2. 역사
stdio.h 라이브러리는 스트림을 사용하여 키보드, 프린터, 터미널 등 물리 장치나 시스템에서 지원하는 다른 종류의 파일을 조작한다. 스트림은 이러한 장치를 동일한 방식으로 처리하기 위한 추상화이다. 모든 스트림은 관련 물리 장치의 차이와 관계없이 유사한 속성을 갖는다.
POSIX 표준에서는 stdio에 대한 확장을 정의하고 있다. 이를 통해 메모리를 자동으로 할당하는 `readline` 함수, `FILE` 형식과 파일 디스크립터를 연결하는 `fileno` 함수와 `fdopen` 함수를 사용하는 것이 가능하게 된다.
3. 주요 기능
C 파일 입출력 기능의 대부분은 C 표준 라이브러리 헤더 파일인 `stdio.h`에 정의되어 있다. (C++의 경우 `cstdio` 헤더에 포함되어 있으며, `std` 네임스페이스를 사용해야 한다.)
이 라이브러리는 키보드, 프린터, 터미널과 같은 물리적 장치 또는 시스템에서 지원하는 다른 모든 유형의 파일과 작동하기 위해 스트림이라고 하는 것을 사용한다. 스트림은 이러한 장치와 통일된 방식으로 상호 작용하기 위한 추상화이다. 모든 스트림은 연결된 물리적 매체의 개별 특성과 관계없이 유사한 속성을 갖는다.
| 기능 | 바이트 문자 | 와이드 문자 | 설명 |
|---|---|---|---|
| 파일 접근 | fopen | 파일을 연다 (윈도우에서는 비-유니코드 파일 이름, 유닉스에서는 UTF-8 파일 이름). | |
| freopen | 기존 스트림으로 다른 파일을 연다. | ||
| fflush | 출력 스트림을 실제 파일과 동기화한다. | ||
| fclose | 파일을 닫는다. | ||
| setbuf | 파일 스트림에 버퍼를 장착시킨다. | ||
| setvbuf | 파일 스트림 크기에 맞게 버퍼를 장착시킨다. | ||
| fwide | 전각 문자와 반각 문자간 파일 스트림 교환. | ||
| 직접 입출력 | fread | 파일에서 데이터를 읽는다. | |
| fwrite | 파일에 데이터를 쓴다. | ||
| 비형식화 입출력 | fgetc, getc | fgetwc, getwc | 파일 스트림으로부터 바이트 또는 와이드 문자를 읽는다. |
| fgets | fgetws | 파일 스트림으로부터 바이트 또는 와이드 문자열을 한 줄 읽는다. | |
| fputc, putc | fputwc, putwc | 파일 스트림에 바이트 또는 와이드 문자를 쓴다. | |
| fputs | fputws | 파일 스트림에 바이트 또는 와이드 문자열을 쓴다. | |
| getchar | getwchar | 표준 입력으로부터 바이트 또는 와이드 문자를 읽는다. | |
| gets | 새 줄이 나오거나 파일 끝까지 갈 때까지 표준 입력으로부터 바이트 문자열을 읽는다. (C99에서 사용 중단, C11에서 삭제) | ||
| putchar | putwchar | 표준 출력으로 바이트 또는 와이드 문자를 쓴다. | |
| puts | 표준 출력으로 바이트 문자열을 쓴다. | ||
| ungetc | ungetwc | 파일 스트림에 바이트 또는 와이드 문자를 제자리에 돌려놓는다. | |
| 형식화 입출력 | scanf, fscanf, sscanf | wscanf, fwscanf, swscanf | 파일 스트림이나 버퍼의 표준 입력으로부터 형식화된 바이트 또는 와이드 문자를 읽는다. |
| vscanf, vfscanf, vsscanf | vwscanf, vfwscanf, vswscanf | 가변 인자 목록을 쓰는 파일 스트림이나 버퍼의 표준 입력으로부터 형식화된 바이트 또는 와이드 문자를 읽는다. | |
| printf, fprintf, sprintf, snprintf | wprintf, fwprintf, swprintf | 파일 스트림이나 버퍼의 표준 출력으로 형식화된 바이트 또는 와이드 문자를 출력한다. | |
| vprintf, vfprintf, vsprintf, vsnprintf | vwprintf, vfwprintf, vswprintf | 가변 인자 목록을 쓰는 파일 스트림이나 버퍼의 표준 출력으로 형식화된 바이트 또는 와이드 문자를 출력한다. | |
| perror | 표준 에러에 현재 에러 설명을 쓴다. | ||
| 파일 위치 조정 | ftell, ftello | 현재 파일 포인터를 되돌려준다. | |
| fseek, fseeko | 파일의 특정 위치로 파일 포인터를 이동한다. | ||
| fgetpos | 파일 포인터를 얻는다. | ||
| fsetpos | 파일의 특정 위치로 파일 포인터를 이동한다. | ||
| rewind | 파일 포인터를 파일의 첫 시작 부분으로 이동한다. | ||
| 에러 조작 | clearerr | 에러를 지운다. | |
| feof | 파일의 끝을 체크한다. | ||
| ferror | 파일 에러를 체크한다. | ||
| 파일 조작 명령 | remove | 파일을 삭제한다. | |
| rename | 파일 이름을 수정한다. | ||
| tmpfile | 임시 파일로 포인터를 되돌려준다. | ||
| tmpnam | 특수 파일 이름을 되돌려준다. | ||
3.1. 파일 접근
C 언어에서 파일 입출력의 기본은 파일을 열고 닫는 것이다. 파일을 열 때는 `fopen()` 함수를 사용하고, 파일을 닫을 때는 `fclose()` 함수를 사용한다. `freopen()` 함수는 이미 열려 있는 스트림을 다른 파일에 연결할 때 사용된다.
`fflush()` 함수는 출력 스트림의 버퍼를 비워 그 내용을 실제 파일에 기록한다. `setbuf()` 함수와 `setvbuf()` 함수는 파일 스트림의 버퍼링 방식을 설정하는 데 사용된다.
`fwide()` 함수는 스트림의 입출력 단위를 바이트(반각 문자) 또는 와이드 문자(전각 문자)로 설정한다. 이 함수는 한국어와 같이 멀티바이트 문자를 처리하는 데 중요하다.
| 함수 | 설명 |
|---|---|
| fopen | 파일을 연다. (윈도우에서는 비-유니코드 파일 이름, 유닉스에서는 UTF-8 파일 이름) |
| freopen | 이미 열려있는 스트림으로 다른 파일을 연다. |
| fflush | 출력 스트림을 실제 파일과 동기화한다. |
| fclose | 파일을 닫는다. |
| setbuf | 파일 스트림에 버퍼를 설정한다. |
| setvbuf | 파일 스트림 크기에 맞게 버퍼를 설정한다. |
| fwide | 파일 스트림을 와이드 문자 입출력과 좁은 문자 입출력 간에 전환한다. |
3.2. 직접 입출력
https://en.cppreference.com/w/c/io/fread fread 함수와 https://en.cppreference.com/w/c/io/fwrite fwrite 함수는 형식 지정 없이 파일에서 데이터를 읽고 쓴다.
| 함수 | 설명 |
|---|---|
| https://en.cppreference.com/w/c/io/fread fread | 파일에서 데이터를 읽는다. |
| https://en.cppreference.com/w/c/io/fwrite fwrite | 파일에 데이터를 쓴다. |
3.3. 형식화되지 않은 입출력
C 언어에서 형식화되지 않은 입출력은 데이터를 있는 그대로 읽고 쓰는 방식이다. 즉, 정수, 실수, 문자열 등의 데이터 형식을 지정하지 않고 바이트 단위로 처리한다.
3.4. 형식화된 입출력
C 언어에서 형식화된 입출력은 데이터를 특정 형식에 맞춰 입출력하는 방식을 의미한다. 이는 C 표준 라이브러리의 `함수 종류 바이트 문자 함수 와이드 문자 함수 설명 형식화된 출력 `printf`, `fprintf`, `sprintf`, `snprintf` `wprintf`, `fwprintf`, `swprintf` 형식 지정자를 사용하여 데이터를 형식화하여 표준 출력, 파일, 문자열 버퍼에 출력한다. `vprintf`, `vfprintf`, `vsprintf`, `vsnprintf` `vwprintf`, `vfwprintf`, `vswprintf` 가변 인자를 사용하여 데이터를 형식화하여 표준 출력, 파일, 문자열 버퍼에 출력한다. 형식화된 입력 `scanf`, `fscanf`, `sscanf` `wscanf`, `fwscanf`, `swscanf` 형식 지정자를 사용하여 표준 입력, 파일, 문자열 버퍼에서 형식화된 데이터를 읽어온다. `vscanf`, `vfscanf`, `vsscanf` `vwscanf`, `vfwscanf`, `vswscanf` 가변 인자를 사용하여 표준 입력, 파일, 문자열 버퍼에서 형식화된 데이터를 읽어온다.
* 바이트 문자 함수:
* `printf`: 형식화된 데이터를 표준 출력(stdout)에 출력한다.
* `fprintf`: 형식화된 데이터를 지정된 파일 스트림에 출력한다.
* `sprintf`: 형식화된 데이터를 문자열 버퍼에 출력한다.
* `snprintf`: `sprintf`와 유사하지만, 버퍼 오버플로우를 방지하기 위해 최대 출력 문자 수를 지정할 수 있다.
* `scanf`: 표준 입력(stdin)으로부터 형식화된 데이터를 읽어온다.
* `fscanf`: 지정된 파일 스트림으로부터 형식화된 데이터를 읽어온다.
* `sscanf`: 문자열 버퍼로부터 형식화된 데이터를 읽어온다.
* 와이드 문자 함수:
* `wprintf`, `fwprintf`, `swprintf`: `printf`, `fprintf`, `sprintf`의 와이드 문자 버전으로, `wchar_t` 형식의 데이터를 처리한다. 한국어와 같은 멀티바이트 문자 처리에 중요하다.
* `wscanf`, `fwscanf`, `swscanf`: `scanf`, `fscanf`, `sscanf`의 와이드 문자 버전이다.
* 가변 인자 함수:
* `vprintf`, `vfprintf`, `vsprintf`, `vsnprintf`: `printf` 계열 함수들의 가변 인자 버전으로, `va_list`를 인자로 받는다.
* `vwprintf`, `vfwprintf`, `vswprintf`: `wprintf` 계열 함수들의 가변 인자 버전이다.
* `vscanf`, `vfscanf`, `vsscanf`: `scanf` 계열 함수들의 가변 인자 버전이다.
* `vwscanf`, `vfwscanf`, `vswscanf`: `wscanf` 계열 함수들의 가변 인자 버전이다.
3.5. 파일 위치 지정
* ftell/ftello: 현재 파일 위치 지시자를 반환한다.
* fseek/fseeko: 파일 위치 지시자를 파일의 특정 위치로 이동한다.
* fgetpos: 파일 위치 지시자를 얻는다.
* fsetpos: 파일 위치 지시자를 파일의 특정 위치로 이동한다.
* rewind: 파일 위치 지시자를 파일의 시작 부분으로 이동한다.
3.6. 오류 처리
* clearerr(): 파일 스트림의 오류 지시자를 지운다.
* feof(): 파일의 끝(EOF)에 도달했는지 확인한다.
* ferror(): 파일 스트림에 오류가 발생했는지 확인한다.
* perror(): 현재 오류 메시지를 표준 에러 스트림(stderr)에 출력한다.
4. 상수 및 변수
C 언어의 표준 입출력 기능은 스트림을 사용하여 키보드, 프린터, 터미널 등 물리 장치나 시스템에서 지원하는 다른 종류의 파일을 조작한다. 스트림은 이러한 장치를 동일한 조작으로 처리하기 위한 추상화에 이용된다. 모든 스트림은 관련 물리 장치의 차이와 관계없이 유사한 속성을 갖는다.
stdio.h영어 헤더에는 파일 입출력과 관련된 상수와 변수들이 정의되어 있다.
4.1. 상수
stdio.h영어 헤더에는 파일 입출력과 관련하여 다음과 같은 상수들이 정의되어 있다.
| 이름 | 설명 |
|---|---|
| EOF | 파일의 끝 상태를 나타내는 음수 정수이다. |
| [http://c-p-p.net/c/stdio.h/bufsiz BUFSIZ] | `setbuf()` 함수에서 사용되는 버퍼의 크기를 나타내는 정수이다. |
| FILENAME_MAX | 열릴 수 있는 모든 파일의 이름을 충분히 저장할 수 있을 정도로 큰 문자형(char) 배열의 크기이다. |
| FOPEN_MAX | 동시에 열려있는 파일들의 개수를 나타내며 최소 8이다. |
| _IOFBF | "input/output fully buffered"의 약어로, 열린 스트림에 블록 버퍼링된 입출력을 요청하는 `setvbuf()` 함수에 전달되는 정수이다. |
| _IOLBF | "input/output line buffered"의 약어로, 열린 스트림에 라인 버퍼링된 입출력을 요청하는 `setvbuf()` 함수에 전달되는 정수이다. |
| _IONBF | "input/output not buffered"의 약어로, 열린 스트림에 대해 버퍼링되지 않은 입출력을 요청하기 위한 `setvbuf()` 함수에 전달되는 정수이다. |
| L_tmpnam | `tmpnam()` 함수에 의해 생성된 임시 파일 이름을 저장할 수 있을 정도의 크기를 가진 문자형(char) 배열의 크기이다. |
| NULL | 널 포인터 상수이다. 메모리 안의 객체의 유효한 주소가 아닌 포인터 값을 나타낸다. |
| SEEK_CUR | 현재 파일 위치에 대해 위치 변경을 요청하는 `fseek()`에 전달되는 정수이다. |
| SEEK_END | 파일의 끝에 대해 위치 조정을 요청하기 위한 `fseek()` 함수에 전달되는 정수이다. |
| SEEK_SET | 파일의 시작 위치를 기준으로 한 위치 지정을 요청하기 위한 `fseek()` 함수에 전달되는 정수이다. |
| TMP_MAX | `tmpnam()` 기능에 의해 만들어지는 특수 파일이름의 최대 길이이다. (최소 25자) |
4.2. 변수
stdio.h영어 헤더에는 다음과 같은 변수들이 정의되어 있다.
| 이름 | 설명 |
|---|---|
| stdin | 표준 입력 스트림으로 나타나는 파일에 대한 포인터. 주로 키보드를 가리킨다. |
| stdout | 표준 출력 스트림으로 나타나는 파일에 대한 포인터. 주로 디스플레이 터미널을 가리킨다. |
| stderr | 표준 에러 스트림으로 나타나는 파일에 대한 포인터. 주로 디스플레이 터미널을 가리킨다. |
5. 멤버 타입
`stdio.h` 헤더에는 다음과 같은 데이터 타입들이 정의되어 있다.
* `FILE` – 파일 핸들이라고도 불리며, 파일이나 텍스트 스트림에 관한 정보를 담은 오파크 타입이다. 다음과 같은 정보들을 포함한다.
* 파일 식별자와 같은 입출력 장치와 관련된 플랫폼 특수 식별자.
* 버퍼.
* 스트림 지향 식별자 (unset, 좁은, 혹은 넓게).
* 스트림 버퍼링 상태 식별자 (버퍼링되지 않음, 라인 버퍼링, 완전 버퍼림).
* I/O 모드 식별자 (입력 스트림, 출력 스트림, 업데이트 스트림).
* 바이너리/텍스트 모드 식별자.
* 파일의 끝 식별자.
* 에러 식별자.
* 현재 스트림 위치 및 멀티바이트 전환 상태 ( `fpos_t` 타입의 객체).
* 재귀 잠금 (C11 요구함).
* `fpos_t` – 파일 내의 특정 위치를 나타내는 비배열 타입이다. 파일의 모든 바이트 위치와 모든 지원되는 멀티바이트 문자 인코딩에서 발생할 수 있는 모든 전환 상태를 담고 있다.
* `size_t` – `sizeof` 오퍼레이터의 결과를 나타낸 unsigned 정수 타입이다.
6. 확장 (POSIX)
POSIX 표준은 `stdio.h`에 대한 몇 가지 확장을 정의한다. 여기에는 메모리를 할당하는 `readline` 함수, `FILE` 객체와 파일 식별자 간의 연결을 만드는 `fileno` 및 `fdopen` 함수, 메모리 내 버퍼를 가리키는 `FILE` 객체를 생성하는 함수 그룹 등이 포함된다.
7. stdio.h의 대안
AT&T 벨 연구소에서 개발한 Sfio(Safe/Fast I/O)는 1991년에 도입되었으며, `stdio.h` 설계의 비일관성, 안전하지 않은 관행 및 비효율성을 개선하는 것을 목표로 했다. Sfio의 기능 중 하나는 스트림에 콜백 함수를 삽입하여 스트림에서 읽거나 스트림에 기록된 데이터의 처리를 사용자 지정할 수 있다는 것이다. Sfio는 1997년에 외부에 공개되었으며, 마지막 릴리스는 2005년 2월 1일이었다.
C++ {{lang 라이브러리는 ISO C++ 표준의 일부이다. ISO C++는 여전히 `stdio.h` 기능을 필요로 한다.
8. 예제
다음은 "myfile"이라는 이진 파일을 열어 파일 안의 다섯 바이트를 읽고 파일을 닫는 C 프로그램 예제이다.
```c
#include
#include
int main(void)
{
char buffer[5] = {0}; /* 초기화 */
int i;
FILE *fp = fopen("myfile", "rb");
if (fp == NULL) {
perror("파일 열기 실패. \"myfile\"");
return EXIT_FAILURE;
}
for (i = 0; i < 5; i++) {
int rc = getc(fp);
if (rc == EOF) {
fputs("읽는 도중 에러 발생.\n", stderr);
return EXIT_FAILURE;
}
buffer[i] = rc;
}
fclose(fp);
printf("파일에는 다음과 같은 글자들이 있습니다. %x %x %x %x %x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
return EXIT_SUCCESS;
}
```
이 코드는 `fopen()` 함수를 사용하여 "myfile" 파일을 이진 읽기 모드("rb")로 연다. 파일 열기에 실패하면 `perror()` 함수를 호출하여 오류 메시지를 출력하고 프로그램을 종료한다. 파일 열기에 성공하면 `getc()` 함수를 사용하여 파일에서 5바이트를 읽어 `buffer` 배열에 저장한다. `getc()` 함수가 `EOF`를 반환하면 파일 읽기 중 오류가 발생한 것이므로, `fputs()` 함수를 사용하여 오류 메시지를 출력하고 프로그램을 종료한다. 5바이트를 모두 읽은 후에는 `fclose()` 함수를 사용하여 파일을 닫고, `printf()` 함수를 사용하여 읽은 내용을 16진수로 출력한다.
다음은 "myfile"이라는 바이너리 파일을 열어 5바이트를 읽은 다음 파일을 닫는 다른 C 프로그램 예제이다.
```c
#include
#include
int main(void) {
char buffer[5];
FILE* fp = fopen("myfile", "rb");
if (fp == NULL) {
perror("파일 \"myfile\" 열기에 실패했습니다");
return EXIT_FAILURE;
}
if (fread(buffer, 1, 5, fp) < 5) {
fclose(fp);
fputs("파일을 읽는 동안 오류가 발생했습니다.\n", stderr);
return EXIT_FAILURE;
}
fclose(fp);
printf("읽은 바이트는: ");
for (int i = 0; i < 5; ++i) {
printf("%02X ", buffer[i]);
}
putchar('\n');
return EXIT_SUCCESS;
}
```
위 코드는 `fopen()` 함수로 "myfile"이라는 이름의 파일을 바이너리 읽기 모드("rb")로 연다. `fread()` 함수는 `fp` 파일 포인터에서 가리키는 파일에서 1바이트(`buffer`의 원소 크기)씩 5개(`buffer`의 길이)의 데이터를 읽어 `buffer` 배열에 저장한다. 만약 `fread()` 함수가 5보다 작은 값을 반환하면, 파일의 끝에 도달했거나 읽기 오류가 발생했음을 의미한다. 이때 `fclose(fp)`로 파일을 닫고 `fputs()`로 오류 메시지를 출력한 후 프로그램을 종료한다.
다음은 hogefile이라는 바이너리 파일을 열어 5바이트를 읽고 파일을 닫는 C 프로그램 예제이다.
```c
#include
#include
int main(void) {
char buffer[5];
FILE* fp = fopen("hogefile", "rb");
if (fp == NULL) {
perror("\"hogefile\"을 열 수 없습니다.");
return EXIT_FAILURE;
}
for (int i = 0; i < 5; i++) {
int rc = getc(fp);
if (rc == EOF) {
fputs("파일을 읽는 중에 오류가 발생했습니다.\n", stderr);
return EXIT_FAILURE;
}
buffer[i] = rc;
}
fclose(fp);
printf("읽은 바이트 열: %x %x %x %x %x\n", buffer[0], buffer[1],
buffer[2], buffer[3], buffer[4]);
return EXIT_SUCCESS;
}
```
위 코드는 `fopen()` 함수를 사용하여 "hogefile" 파일을 이진 읽기 모드("rb")로 연다. 파일 열기에 실패하면 `perror()` 함수를 호출하여 오류 메시지를 출력하고 프로그램을 종료한다. 파일 열기에 성공하면 `getc()` 함수를 사용하여 파일에서 5바이트를 읽어 `buffer` 배열에 저장한다. `getc()` 함수가 `EOF`를 반환하면 파일 읽기 중 오류가 발생한 것이므로, `fputs()` 함수를 사용하여 오류 메시지를 출력하고 프로그램을 종료한다. 5바이트를 모두 읽은 후에는 `fclose()` 함수를 사용하여 파일을 닫고, `printf()` 함수를 사용하여 읽은 내용을 16진수로 출력한다.
위 예제 코드들은 모두 `