• Linux C 讲解系统调用readdir, readdir_r 以及如何遍历目录下的所有文件


    readdir与readdir_r简要说明

    readdir可以用来遍历指定目录路径下的所有文件。不过,不包含子目录的子文件,如果要递归遍历,可以使用深度遍历,或者广度遍历算法。
    readdir_r 是readdir的可重入版本,线程安全。readdir因为直接返回了一个static的struct dirent,因此是非线程安全。

    readdir如何遍历目录子文件?

    1. opendir打开目录

    opendir有2个版本:opendir,fopendir。前者参数为目录对应字符串,后者参数为目录对应已打开文件描述符。

    #include <sys/types.h>
    #include <dirent.h>
    
    DIR *opendir(const char *name);
    DIR *fdopendir(int fd);
    

    用法模型:

    DIR *dirp;
    const char *base_dir = "/home/martin/document";
    
    if ((dirp = opendir(base_dir)) != NULL) {
      perror("opendir error");
      return -1;
    }
    
    // 调用readdir遍历目录子文件
    ...
    
    closedir(base_dir);
    

    2. readdir遍历目录子文件

    readdir需要一个已打开(调用opendir)的DIR对象作为参数。

    #include <dirent.h>
    
    struct dirent *readdir(DIR *dirp);
    
    int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
    

    dirent 结构定义

    struct dirent {
       ino_t          d_ino;       /* inode number i节点编号 */
       off_t          d_off;       /* not an offset; see NOTES 早期文件系统中,telldir返回文件在目录内的偏移 */
       unsigned short d_reclen;    /* length of this record dirent 记录的实际长度 */
       unsigned char  d_type;      /* type of file; not supported
                                      by all filesystem types 文件类型 */
       char           d_name[256]; /* filename 文件名 */
    };
    

    成员介绍:

    • d_ino i节点编号,操作系统用来识别文件的,每个文件都有一个inode number(参见Inode详解

    • d_off 早期文件系统中,文件系统使用平面表格,telldir返回文件在目录内的偏移,而d_off就代表这个偏移的缓存。应用把该值当做一个不透明的值即可。

    • d_reclen 记录的实际长度,并非sizeof(struct dirent)的值,而是sizeof(struct dirent) - sizeof(name) + {strlen(name) 补齐8字节对齐}。

    • d_type 文件类型,并非所有文件系统都支持。另外一种支持更好的查看文件类型的方式是,使用stat系统调用的st_mode。

    • d_name 文件名。在代码中可能会看到后面有注释/* We must not include limits.h! */,这是因为POSIX.1时,未规定路径名长度NAME_MAX,而POSIX.1要求d_name最多支持255个字符,255这个魔法数字是一个难以移植的特定数字。limits.h中定义的NAME_MAX是在POSIX.1-2001加入的。

    遍历子文件模型:

    DIR *dirp;
    const char *base_dir = "/home/martin/document";
    
    if ((dirp = opendir(base_dir)) != NULL) {
      perror("opendir error");
      return -1;
    }
    
    // 调用readdir遍历目录子文件
    struct dirent *dp;
    while ((dp = readdir(dirp)) != NULL) {
      // 读取、打印文件名、文件类型等信息
      printf dp->d_name, d_type
    }
    
    closedir(base_dir);
    

    3. readdir完整示例

    要遍历的目录:/home/martin/documents
    执行$ tree -L 1

    执行$ls -al

    readdir遍历指定目录,并打印示例代码

    int list_file(const char *base_dir)
    {
        DIR *dirp;
        struct dirent* dp;
    
        // 打开目录
        if ((dirp = opendir(base_dir)) == NULL) {
            perror("opendir error");
            return -1;
        }
        
        printf("sizeof(dirent) = %ld
    ", sizeof(struct dirent) - 256);
    
        printf("%-20s %10s %25s %15s %15s %-s
    ", "type", "d_ino", "d_off", "d_reclen", "len", "filename");
    
        while ((dp = readdir(dirp)) != NULL) {
            // 忽略当前目录"."和上一级目录".."(父目录)
            if (0 == strcmp(dp->d_name, ".") || 0 == strcmp(dp->d_name, ".."))
                continue;
    
            // 读取文件类型
            char type[50];
            switch (dp->d_type) {
                case DT_DIR: // a directory
                    snprintf(type, sizeof(type), "%s", "directory");
                    break;
                case DT_REG: // a regular file
                    snprintf(type, sizeof(type), "%s", "regular file");
                    break;
                case DT_BLK: // a block device
                    snprintf(type, sizeof(type), "%s", "block device");
                    break;
                case DT_CHR: // a character device
                    snprintf(type, sizeof(type), "%s", "character device");
                    break;
                case DT_FIFO: // a named pipe (FIFO)
                    snprintf(type, sizeof(type), "%s", "named pipe (FIFO)");
                    break;
                case DT_LNK: // a symbolic link
                    snprintf(type, sizeof(type), "%s", "symbolic link");
                    break;
                case DT_SOCK: // a UNIX domain socket
                    snprintf(type, sizeof(type), "%s", "UNIX domain socket");
                    break;
                default: // DT_UNKNOWN - file type unknown
                    snprintf(type, sizeof(type), "%s", "file type unknown");
                    break;
            }
            printf("%-20s %10lu %25ld %15u %15ld %-s
    ", type, dp->d_ino, dp->d_off, dp->d_reclen, strlen(dp->d_name), dp->d_name);
        }
    
        // 关闭目录
        closedir(dirp);
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        list_file("/home/martin/Documents");
        return 0;
    }
    

    运行结果

    sizeof(dirent) = 24
    type                      d_ino                     d_off        d_reclen             len filename
    regular file             292970       3024556464499973779              40              13 list12.dKLpEX
    directory               1180001       3470762240268728258              32               8 winshark
    regular file             293625       4800707232840484128              32               8 list.txt
    regular file             292872       9057525276248794967              64              37 RFC959_FTP传输协议(中文版).pdf
    regular file             288738       9223372036854775807              40              14 rfc114.txt.pdf
    

    4. readdir_r完整示例

    readdir_r遍历指定目录,并打印示例代码

    #define NAME_MAX_PORTABLE     // 文件名称可移植
    
    int list_file_r(const char *base_dir)
    {
        DIR *dirp;
    
    #ifndef NAME_MAX_PORTABLE // 未定义 文件名称可移植
        struct dirent d1, d2;
        struct dirent *entryp, *res;
        entryp = &d1;
        res = &d2;
    #else // 定义了 文件名称可移植
        struct dirent *entryp, *res;
        long name_max = pathconf(base_dir, _PC_NAME_MAX);
    
        if (name_max == -1) {
            name_max = 255; // guess
        }
        int len = offsetof(struct dirent, d_name) + name_max + 1; // + 1 for NULL terminate byte
        entryp = malloc(len);
        res = malloc(len);
    #endif
    
        // 打开目录
        if ((dirp = opendir(base_dir)) == NULL) {
            perror("opendir error");
            return -1;
        }
    
        printf("%-20s %10s %25s %15s %15s %-s
    ", "type", "d_ino", "d_off", "d_reclen", "len", "filename");
        int ret;
        while(1) {
            if ((ret = readdir_r(dirp, entryp, &res)) > 0) {
                fprintf(stderr, "readdir_r error: %d
    ", ret);
                perror("readdir_r error");
                return -1;
            }
    
            if (res == NULL)
                break;
            if (strcmp(entryp->d_name, ".") == 0 || strcmp(entryp->d_name, "..") == 0)
                continue;
    
            // 忽略当前目录"."和上一级目录".."(父目录)
            if (0 == strcmp(entryp->d_name, ".") || 0 == strcmp(entryp->d_name, ".."))
                continue;
            // 读取文件类型
            char type[50];
            switch (entryp->d_type) {
                case DT_DIR: // a directory
                    snprintf(type, sizeof(type), "%s", "directory");
                    break;
                case DT_REG: // a regular file
                    snprintf(type, sizeof(type), "%s", "regular file");
                    break;
                case DT_BLK: // a block device
                    snprintf(type, sizeof(type), "%s", "block device");
                    break;
                case DT_CHR: // a character device
                    snprintf(type, sizeof(type), "%s", "character device");
                    break;
                case DT_FIFO: // a named pipe (FIFO)
                    snprintf(type, sizeof(type), "%s", "named pipe (FIFO)");
                    break;
                case DT_LNK: // a symbolic link
                    snprintf(type, sizeof(type), "%s", "symbolic link");
                    break;
                case DT_SOCK: // a UNIX domain socket
                    snprintf(type, sizeof(type), "%s", "UNIX domain socket");
                    break;
                default: // DT_UNKNOWN - file type unknown
                    snprintf(type, sizeof(type), "%s", "file type unknown");
                    break;
            }
            printf("%-20s %10lu %25ld %15u %15ld %-s
    ", type, entryp->d_ino, entryp->d_off, entryp->d_reclen, strlen(entryp->d_name), entryp->d_name);
        }
    
        printf("exit list_file_r
    ");
    
    #ifdef NAME_MAX_PORTABLE
        // 释放资源
        free(res);
        free(entryp);
    #endif
    
        // 关闭目录
        closedir(dirp);
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        list_file_r("/home/martin/Documents");
        return 0;
    }
    

    运行结果

    type                      d_ino                     d_off        d_reclen             len filename
    regular file             292970       3024556464499973779              40              13 list12.dKLpEX
    directory               1180001       3470762240268728258              32               8 winshark
    regular file             293625       4800707232840484128              32               8 list.txt
    regular file             292872       9057525276248794967              64              37 RFC959_FTP传输协议(中文版).pdf
    regular file             288738       9223372036854775807              40              14 rfc114.txt.pdf
    exit list_file_r
    
  • 相关阅读:
    事务与事务隔离级别
    TNS12535: TNS: 操作超时
    11g的exp导出空表提示EXP00011: SCOTT.TEST1 不存在
    oracle中chr含义
    SQL Server 2008 System Views Map
    SQL Server Execution Plans eBook
    生成建表脚本(V3.0)
    SQL Server 2008 通过配置数据库邮件实现发送邮件功能
    MSSQL2005中的非公开存储过程sp_msdependencies
    SQL Server Tacklebox Free eBook
  • 原文地址:https://www.cnblogs.com/fortunely/p/15178264.html
Copyright © 2020-2023  润新知