차근차근/C

dirent.h

예쁜꽃이피었으면 2014. 7. 29. 00:59

 http://ods81.tistory.com/entry/direnth

 

 

재밌는 C 프로그래밍에 대한 여행
오늘은 디렉토리 관련 함수에 대해 알아보도록 하겠다..




출처 : 
http://blog.naver.com/beyondlegend?Redirect=Log&logNo=110029848992


오늘은 디렉토리 관련 함수를 다루어 볼까 합니다.

디렉토리 안에는 일반적으로 파일이 보관되어 있습니다. 이미 알고 계시듯이 유닉스계열에서는 모든것을 파일로 취급합니다.  일반파일은 물론이고 하드디스크나 USB 같은 디바이스, 그리고 네트웍 인터페이스도 파일로 취급할 수 있습니다. (자세한 내용은 제 블로그의 리눅스이야기 부분을 참고하시기 바랍니다 ^^) 이렇듯 모든것을 파일로 취급할때 얻을수 있는 장점은 일관된 프로그래밍 인터페이스를 가지고 접근할 수 있다는것입니다. 즉, 리눅스에서는 일반파일에 입출력을 하는것이나 네트웍을 이용하는 것이나 일관되게 open() / read() / write() / close() 등을 사용할 수 있게 해줍니다.

그럼 디렉토리 관련 함수와 유사성을 찾기위해 우선 파일 관련 프로그래밍을 하는경우를 한번 보겠습니다. 아래는 test.txt 라는 파일을 읽어 화면에 출력해주는 간단한 프로그램입니다.

===============================================================================
#include <stdio.h>
int main()
{
    FILE* fp = NULL;
    char  buffer[100];

    if  ( (fp=fopen("test.txt","r")) == NULL )
    {
      printf("파일을 열수 없습니다...\n");
      return -1;
    }

    while  (  fgets(buffer, 100, fp)  !=  NULL )
      printf("%s", buffer);

    fclose(fp);
  }===============================================================================

위 소스에서 보듯이 파일관련 프로그래밍을 할때는 일정한 순서가 있습니다.

첫째, 파일포인터를 선언해야 합니다  (FILE* fp = NULL)
둘째, 선언된 파일포인터를 이용해 사용할 파일을 open 해야 합니다  
(
fp = fopen("test.txt", "r"))
셋째, open 된 파일에서 내용을 읽습니다  
(
while ( fgets(buffer, 100, fp) == NULL ) )
넷째, 모든 작업을 마치면 open 된 파일을 닫아야 합니다.  (fclose(fp))

디렉토리 관련 프로그래밍도 한부분만 제외하고는 파일과 같은 순서로 프로그래밍이 가능합니다.

첫째, 디렉토리 포인터를 선언해야 합니다.
둘째, 선언된 디렉토리 포인터를 이용해 사용할 디렉토리를 open 해야 합니다.
셋째, open 된 디렉토리에서 내용을 읽습니다
넷째, 읽어드린 내용을 분석합니다. 즉, 읽어드린 내용이 파일인지, 또는 디렉토리인지 구분해서 적절한 작업을 해 줍니다.
다섯째, 모든작업을 마치면 open 된 디렉토리를 닫아야 합니다.

위에서 보듯이 네번째 항목만 제외하면  파일 프로그래밍과 아주 비슷합니다. 네번째 항목이 차이나는 이유는 디렉토리안에는 파일뿐만이 아니라 디렉토리도 포함될수 있기 때문에 이런것을 구별해주어야 합니다.

그럼 지금부터 해당하는 디렉토리 함수를 알아보겠습니다.

[1] 디렉토리 포인터 DIR*
C 언어 계열 (C++ 마찬가지입니다) 에서, 파일을 나타내는 것이 FILE 이듯이  디렉토리를 나타내는 구분자로 DIR 이라는 예약어를 사용합니다. (DIR 은 구조체로서 디렉토리의 많은 정보를 저장할 수 있는 필드로 구성되어 있습니다) 즉. 디렉토리 관련 작업을 하실때는 우선 DIR* dp; 와 같은 포인터 선언을 해주셔야 합니다

[2] 디렉토리를 열어주는 opendir(), 닫아주는 closedir()
파일과 마찬가지로 디렉토리를 사용하기 위해서는 우선 open이 되어야 합니다. 이렇게 디렉토리를 열어주는 함수가 opendir(), 닫아주는 함수가 closedir()  입니다. opendir() 과 closedir() 의 완전한 선언은 다음과 같습니다

#include  <sys/types.h>
#include  <dirent.h>

DIR*  opendir(const  char*  name);
int    closedir(DIR*  dp);

opendir() 함수는 열고자하는 디렉토리 경로를 입력으로 받고 성공하면 DIR 구조체에 대한 포인터를 반환합니다. 만약 해당디렉토리 경로가 존재하지 않거나 퍼미션등의 이유로 실패한다면 NULL 을 반환합니다. 그리고 closedir() 은 열려있는 디렉토리를 안전하게 닫아줍니다.

다음은 opendir() 과 closedir() 의 간단한 사용법입니다.

========================================================================================

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main(int argc, char **argv)
{
    DIR *dp = NULL;

    if((dp = opendir(argv[1])) == NULL) {
        printf("Can't open %s\n", argv[1]);
        return -1;
    }

    ................

    closedir(dp);

========================================================================================


 

[3] 디렉토리 엔트리를 읽어들이는 readdir()
open 된 디렉토리에서 각각의 항목, 즉 엔트리를 읽어들이는 함수가 readdir() 입니다. 디렉토리안에는 많은 파일과 디렉토리가 포함될수 있습니다. 그러나 readdir() 함수는 한번에 하나의 엔트리만을 읽어들이기 때문에 주로 while 문과 같은 반복문과 함게 쓰이는 경우가 많습니다. 아래는 readdir() 의 완전한 선언입니다.

#include <sys/types.h>
#include <dirent.h>

struct  dirent*  readdir(DIR*  dp);

readdir() 함수는 디렉토리 포인터 dp 가 가리키는 곳, 즉 해당 디렉토리에서 디렉토리 엔트리에 대한 포인터를 반환하고 더이상의 엔트리가 없으면 NULL 을 리턴합니다. 쉽게 말하면 해당 디렉토리에서 하나의 엔트리정보가 구조체 포인터 struct dirent* 에 포함되어 반환됩니다.

dirent 구조체에는 다음과 같은 정보가 실려있습니다.

struct   dirent
{
    long                  d_ino;                               // inode 넘버
    off_t                  d_off;                                // offset
    unsigned short   d_reclen;                          // d_name 길이
    char                  d_name[NAME_MAX+1];    // 파일이름
}

여기서 우리가 관심있게 봐야할 필드는 d_name 입니다. d_name 에는 해당디렉토리의 엔트리이름, 즉 파일이름 일수도 있고 하위디렉토리 이름일수도 있습니다.

아래는 readdir() 을 이용한 간단한 프로그램입니다. 소스에서 보듯이 디렉토리안에는 하나이상의 엔트리가 있을수 있으므로 while 문을 사용하였습니다..

========================================================================================

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main(int argc, char **argv)
{
    DIR *dp = NULL;
    struct dirent *entry = NULL;

    if((dp = opendir(argv[1])) == NULL) {
        printf("Can't open %s\n", argv[1]);
        return -1;
    }

    while( (entry = readdir(dp)) != NULL ) {
    }

    closedir(dp);

========================================================================================

[4] 파일정보를 알려주는 lstat()
파일관련 프로그래밍과 디렉토리 관련 프로그래밍을 구별하게 해주는 부분이 바로 lstat() 을 사용하는 부분입니다 (숫자 1이 아닌 소문자 l 입니다..^^)  이미 말한것처럼 디렉토리 안에는 파일뿐만이 아니라 디렉토리 및 특수파일등도 포함될 수 있기 때문에 readdir() 로 부터 읽어 들인 엔트리에서 구체적인 정보를 뽑아 내어야 합니다. 이런것을 가능하게 해주는 함수가 바로 lstat() 입니다. 아래는 lstat() 에 대한 완전한 선언부분 입니다.

#include  <sys/types.h>
#include  <sys/stat.h>
#include  <unistd.h>

int   lstat(const  char* path,  struct  stat*  buf);

위 선언에서 보듯이 입력되는 path 를 이용하여 (-파일이름입니다-)  buf 라는곳에 정보를 넘겨줍니다.  즉, struct stat 이라는 구조체를 선언하고 그 구조체의 포인터를 넘겨주면 해당 파일의 자세한 정보를 알려주는 함수입니다. stat 구조체의 내용은 다음과 같습니다 (엄청 복잡하네요..^^)


 

struct  stat

{
    dev_t         st_dev;      /* device */
    ino_t         st_ino;      /* inode */
    mode_t        st_mode;     /* protection */
    nlink_t       st_nlink;    /* number of hard links */
    uid_t         st_uid;      /* user ID of owner */
    gid_t         st_gid;      /* group ID of owner */
    dev_t         st_rdev;     /* device type (if inode device) */
    off_t         st_size;     /* total size, in bytes */
    blksize_t     st_blksize;  /* blocksize for filesystem I/O */
    blkcnt_t      st_blocks;   /* number of blocks allocated */
    time_t        st_atime;    /* time of last access */
    time_t        st_mtime;    /* time of last modification */
    time_t        st_ctime;    /* time of last change */

많은 필드중 우선 관심을 가질만한 필드는 두개입니다. 바로 st_mode 와 st_size 입니다. 우선 st_size는 리턴되는 엔트리의 총 크기를 바이트로 나타낸 값입니다. 그리고 st_mode 는 파일 형식정보, 즉, 리턴된것이 디렉토리인지, 아니면 일반파일인지등의 정보가 들어가 있습니다. 말로 적으려니까 좀 어렵네요..백문이 불여일타라고, 직접 소스를 보면서 확인해보겠습니다.

========================================================================================

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    DIR *dp = NULL;
    struct dirent *entry = NULL;
    struct stat buf;

    if( (dp = opendir(argv[1])) == NULL ) {
        printf("Can't open %s\n", argv[1]);
        return -1;
    }

    while( (entry = readdir(dp)) != NULL ) {
        lstat( entry->d_name, &buf );

        if( S_ISDIR(buf.st_mode) )
            printf("[Directory Name: %s\n", entry->d_name);
        else if( S_ISREG(buf.st_mode) )
            printf("[File Name: %s\n", entry->d_name);
    }

    closedir(dp);
}
========================================================================================

위 소스에서 S_ISDIR(...), S_ISREG(...) 등은 buf.st_mode 의 값이 디렉토리 인지, 아님 일반파일인지 구별해주는 매크로입니다. (man lstat 해보시면 자세히 알수 있습니다).


반응형