파일을 open() 함수를 사용하여 구한 정수 값을 파일 디스크립터라고 한다면, fopen()으로 구한 FILE * 포인터를 파일 스트림 포인터라고 합니다. 파일 디스크립터와 파일 스트림. 어차피 파일을 다루기 위한 정보입니다만, 디스크립터이네, 스트림 포인터이네 라고 새로운 말을 만들어 내는 것을 보면 또 저 같이 머리 안 좋은 사람을 불편하게 하려고 어려운 말을 만들어 낸다 싶습니다.

 

그러나 이해해 주어야 하는 것이, 구별되는 차이가 다만 정수와 FILE 구조체 포인터만은 아니기 때문입니다. 생성되는 정보뿐만 아니라 사용할 수 있는 함수까지 다르기 때문이죠.

 

혹시, 이럴 때 있지 않나요? 저는 스트림보다는 디스크립터를 사용하는데, 때로 오픈한 파일의 정보를 구하고 싶은데, 저수준 함수만 사용할 수 있는 디스크립터로는 구하기 어려울 때가 있습니다. 예를 들어 파일 크기를 구한다거나 하는 것 말이죠.

스트림 포인터를 이용하면 간단하게 파일 크기를 구할 수 있지요.

#include <stdio.h>

int main( void)
{
    FILE   *fp;
        
    fp  = fopen( "main.c", "r");
    
    fseek( fp, 0L, SEEK_END);
    printf( "file size=%d\n", ftell( fp));
    
    fclose( fp);
}

다시 말씀 드립니다만, fseek()나 ftell()은 모두 표준 입출력 함수로 파일 스트림 포인터를 가지고 있어야 사용이 가능합니다. 그렇다면 open() 함수로 열어 사용했을 때에는 다시 같은 파일을 fopen() 으로 열기를 해야 할까요? 흠…좀 불편하겠죠. 파일 이름도 넘겨 주어야 하고 말이죠. 그러나 다행이 fdopen() 함수를 사용하면 됩니다.

FILE *fdopen(int fildes, const char *mode);

저수준 함수를 사용하다가 표준 함수를 사용할 필요가 있다면 편리하겠지요.

#include <stdio.h>
#include <fcntl.h>
                   
int main( void)
{
    int     fd;
    FILE   *fp;
        
    fd  = open( "main.c", O_RDONLY, 0644);
    fp  = fdopen( fd, "r");
    
    fseek( fp, 0L, SEEK_END);
    printf( "file size=%d\n", ftell( fp));
    
    fclose( fp);
}

그런데 문제가 있습니다. fdopen으로 열기를 했다면 당연히 생성된 FILE * 에 자원을 제거하기 위해서라도 fclose()를 호출해야 하는데, 그렇다면 open()으로 디스크립터를 구했으니 close()로 닫아 주어야 할까요? 이 부분이 좀 아쉬운데요, 위의 예를 보셔도 아시겠습니다만, fclose()를 호출하여 파일을 닫았다면 open()으로 구한 디스크립터도 함께 닫히기 때문에 close()를 호출할 수 없고, 이런 사정으로 아래와 같이 함수로 구현해서 사용하기에는 불안합니다. 함수 호출 후에 파일을 사용할 수 없기 때문이죠.

#include <stdio.h>
#include <fcntl.h>
 
int file_size( int fd)      // 파일 디스크립터로 파일 크기를 구합니다.                 
{
    FILE   *fp;
    int     sz_file;
        
    fp  = fdopen( fd, "r");
    fseek( fp, 0L, SEEK_END);
    sz_file = ftell( fp);
    fclose( fp);

    return( sz_file);
}
int main( void)
{
    char    buff[1024];
    int     fd;
        
    fd  = open( "main.c", O_RDONLY, 0644);

    printf( "file size=%d\n", file_size( fd));
    
    lseek( fd, 0, SEEK_SET);
    
    while( 0 < read( fd, buff, 1024))   // 파일 내용을 출력
        printf( "%s\n", buff);
    
    close( fd);
}

실행해 보시면 아시겠습니다만, 파일 크기는 출력되어도 파일 내용은 출력되지 않습니다. 이미 file_size() 함수에서 fclose()를 호출하여 파일을 닫았기 때문이죠. 그렇다고 안 할수도 없습니다. 함수를 호출할 때 마다 FILE * 정보가 계속 생성되고 누적될 테니 말이죠.

 

이 문제를 해결하려면 디스크립터를 하나 더 복사해서, 복사본을 사용하면 어떨까요? 디스크립터도 dup()함수를 이용하면 복사본을 만들 수 있습니다.

int dup(int oldfd);

그래서 아래와 같이 구성한다면 문제를 해결할 수 있습니다.

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int file_size( int fd)      // 파일 디스크립터로 파일 크기를 구합니다.
{
    FILE   *fp;
    int     sz_file;

    fp  = fdopen( fd, "r");
    fseek( fp, 0L, SEEK_END);
    sz_file = ftell( fp);

    lseek( fd,  0, SEEK_SET);   // 반드시 lseek()로 읽기 포인터를 처음으로

    fclose( fp);

    return( sz_file);
}
int main( void)
{
    char    buff[1024];
    int     fd;

    fd  = open( "main.c", O_RDONLY, 0644);

    printf( "file size=%d\n", file_size( dup( fd)));


    while( 0 < read( fd, buff, 1024))   // 파일 내용을 출력
        printf( "%s\n", buff);

    close( fd);
}

자, file_size()를 호출할 때, 디스크립터의 복사본을 넘긴 것을 보실 수 있습니다. 실행해 보시면 파일 크기부터 파일 내용까지 이상없이 출력되는 것을 보실 수 있습니다. 저수준 입력출 함수를 사용하시다가 표준 파일 입출력 함수 사용이 필요하시면 참고하세요. ^^