[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; }