[Linux] 파일 입출력 함수 (fopen / fgetc / fputc / fclose / fgets / fputs / fread / fwrite)
Synopsis
Linux에서 파일을 읽고 쓰기를 하는 동작은 매우 잦으며 중요하다. 이 때 사용되는 함수들을 간단하게 소개한다.
| Name | Form | Description |
|---|---|---|
| fopen | FILE *fopen(const char *pathname, const char *mode); | File descriptor 열기 |
| fgetc | int fgetc(FILE *stream); | fd를 통해 1 byte 읽기 |
| fputc | int fputc(int c, FILE *stream); | fd에 문자 쓰기 |
| fclose | int fclose(FILE *stream); | File descriptor 닫기 |
| fgets | char *fgets(char *s, int size, FILE *stream); | 파일을 line 단위로 읽어주며, 파일의 끝을 만나면 0을 return 한다. |
| fputs | int fputs(const char *s, FILE *stream); | Line 단위로 출력해준다. |
| fread | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); | 사용자가 지정한 크기 단위로 파일을 읽는다. |
| fwrite | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); | 사용자가 지정한 크기 단위로 파일을 쓴다. |
Example
#include <stdio.h>
int main(int argc, char **argv) {
FILE *fp;
int ch;
int count=0;
fp = fopen(argv[1], "r");
while((ch = fgetc(fp)) != EOF) {
fputc(ch, stdout);
count++;
}
printf("count=%d\n", count );
fclose(fp);
return 0;
}
# example.txt에는 Hello World가 쓰여있는 상태 $ ./a.out example.txt Hello World count=12
위 예시 코드는 읽고자 하는 파일명을 받아서 해당 파일 내용을 출력하고, 파일 크기를 출력한다. fgetc를 통해 1 byte씩 출력하게 되는데, return하는 타입을 보면 int 타입이다.
이유는, 만약 1 byte라고 하면 EOF (-1)가 가리키는 값인 ‘1111_1111’ 가 ‘FF’라고 표기되는 특수문자를 만났을 때 EOF와 혼동 될 수 있기 때문이다.
만약 int ch 를 char ch 로 변경 했다면 아래와 같은 결과가 나타난다.
$ ./a.out example.txt
Hello World
count=12
$ ./a.out a.out ELF> @8 @@@888
hh/lib64/ld-linux-x86-64.so.2GNUGNU?¬±·فڵ
)_ +%A{
2"libc.so.6fopenprintffgetcstdoutfputcfclose__cxa_finalize__libc_start_mainGLIBC_2.2.5_ITM_deregisterTMCloneTable__gm ֠枆tart___ITM_registerTMCloneTableui S
° ¸ ƠH H
count=1568
$ ls -al
total 20
drwxrwxr-x 1 shumin shumin 48 Apr 11 23:06 .
drwxrwxr-x 1 shumin shumin 152 Apr 11 21:35 ..
-rwxrwxr-x 1 shumin shumin 8520 Apr 11 23:06 a.out
-rw-rw-r-- 1 shumin shumin 12 Apr 11 23:00 example.txt
-rw-rw-r-- 1 shumin shumin 242 Apr 11 23:06 file_1.c
a.out은 binary file로, text editor로 열었을 때 다양한 특수문자로 나타나면서, 0xFF와 만나면서, 실제 마지막 파일 마지막을 만나기 전에 EOF로 혼동하여 종료한다.
따라서 다시 원래대로 수정하고 binary 크기를 확인하면 정상적으로 나타나는 것을 확인 할 수 있다.
$ ./a.out a.out count=8432
실제 EOF는 파일 data에 적혀서 구분자가 있는 값이 아니라, 모든 파일은 크기를 i-node 구조체에 저장하고 있고, 파일을 끝까지 읽었는지 안 읽었는지는 크기를 통해 알 수 있다. EOF는 라이브러리가 리턴하는 값이다.
Example: fgets / fputs
fgetc / fputc보다 더 빠르게 입출력하는 함수로, line 단위로 line buffer에 읽고 써서 속도를 향상시킨다.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
FILE *fp;
char buf[100];
fp = fopen(argv[1], "r");
while(fgets(buf, sizeof(buf), fp)) {
usleep(100000);
fputs(buf, stdout);
fflush(stdout);
}
fclose(fp);
return 0;
}
fread / fwrite
fgets / fputs 는 buffer 크기가 line 단위라는 제약이 있는데, fread / fwrite을 사용하면 원하는 크기만큼 line buffer 크기를 잡을 수 있다.
fread
| Argument | Description |
|---|---|
| void* ptr | 버퍼 포인터 |
| size_t size | 읽어들일 원소의 크기로 단위는 바이트 이다. 예를 들어 버퍼 원소 크기가 4 이면 하나의 원소의 크기는 4 바이트 임을 일컫는다. |
| size_t count | 읽어들일 원소들의 개수 |
| FILE* stream | 데이터를 입력받을 스트림의 FILE 객체를 가리키는 포인터 |
| Return | Description |
|---|---|
| size_t | 원소 개수를 리턴한다 |
Example
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
FILE *fp;
char buf[100];
fp = fopen(argv[1], "r");
while(fread(buf, 1, sizeof(buf), fp)) {
usleep(100000);
fwrite(buf, 1, sizeof(buf), stdout);
fflush(stdout);
}
fclose(fp);
return 0;
}
$ ./a.out file_6.c
include
include
int main(int argc, char **argv) {
FILE *fp;
char buf[100];
fp = fopen(argv[1], "r");
while(fread(buf, 1, sizeof(buf), fp)) {
usleep(100000);
fwrite(buf, 1, sizeof(buf), stdout);
fflush(stdout);
}
fclose(fp);
return 0;
}
eep(100000);
fwrite(buf,shumin@mercury:~/workspace/
$ ls -l file.c
-rw-rw-r-- 1 shumin shumin 285 Apr 12 23:06 file.c
그런데 수행결과를 보면 쓰레기 문자들이 함께 출력된 것을 볼 수 있다. 이유는 fwrite을 할 때 마지막에 buffer 크기를 다 채우지 못할 때 이전 data가 출력이 되기 때문이다.
이를 위해서 아래와 같이 수정하면 정상적으로 동작한다.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
FILE *fp;
char buf[100];
int size;
fp = fopen(argv[1], "r");
while(size = fread(buf, 1, sizeof(buf), fp)) {
usleep(100000);
fwrite(buf, 1, size, stdout);
fflush(stdout);
}
fclose(fp);
return 0;
}