Linux,  Programming

[Linux] 파일 입출력 함수 (fopen / fgetc / fputc / fclose / fgets / fputs / fread / fwrite)

Synopsis

Linux에서 파일을 읽고 쓰기를 하는 동작은 매우 잦으며 중요하다. 이 때 사용되는 함수들을 간단하게 소개한다.

NameFormDescription
fopenFILE *fopen(const char *pathname, const char *mode);File descriptor 열기
fgetcint fgetc(FILE *stream);fd를 통해 1 byte 읽기
fputcint fputc(int c, FILE *stream);fd에 문자 쓰기
fcloseint fclose(FILE *stream);File descriptor 닫기
fgetschar *fgets(char *s, int size, FILE *stream);파일을 line 단위로 읽어주며, 파일의 끝을 만나면 0을 return 한다.
fputsint fputs(const char *s, FILE *stream);Line 단위로 출력해준다.
freadsize_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);사용자가 지정한 크기 단위로 파일을 읽는다.
fwritesize_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 chchar 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

ArgumentDescription
void* ptr버퍼 포인터
size_t size읽어들일 원소의 크기로 단위는 바이트 이다. 예를 들어 버퍼 원소 크기가 4 이면 하나의 원소의 크기는 4 바이트 임을 일컫는다.
size_t count읽어들일 원소들의 개수
FILE* stream데이터를 입력받을 스트림의 FILE 객체를 가리키는 포인터
ReturnDescription
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;
}

Leave a Reply

Your email address will not be published. Required fields are marked *