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

[an error occurred while processing the directive]

readdir与readdir_r简要说明

readdir可以用来遍历指定目录路径下的所有文件。不过,不包含子目录的子文件,如果要递归遍历,可以使用深度遍历,或者广度遍历算法。

readdir_r 是readdir的可重入版本,线程安全。readdir因为直接返回了一个static的struct dirent,因此是非线程安全。

注意:readdir_r 已经废弃。在POSIX.1标准中,并没有要求readdir是线程安全的,但在现代实现中(包括glibc的实现),readdir是线程安全的。而readdir_r 的存在,会让人误解readdir为非线程安全的,因此2.23以后,readdir_r 被废弃。

参考 Don't use readdir_r if deprecated #72

readdir如何遍历目录子文件?

1. opendir打开目录

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

#include

#include

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

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\n", sizeof(struct dirent) - 256);

printf("%-20s %10s %25s %15s %15s %-s\n", "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\n", 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);

#endif

// 打开目录

if ((dirp = opendir(base_dir)) == NULL) {

perror("opendir error");

return -1;

}

printf("%-20s %10s %25s %15s %15s %-s\n", "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\n", 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\n", type, entryp->d_ino, entryp->d_off, entryp->d_reclen, strlen(entryp->d_name), entryp->d_name);

}

printf("exit list_file_r\n");

#ifdef NAME_MAX_PORTABLE

// 释放资源

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

[an error occurred while processing the directive]
Copyright © 2088 米策网游动态中心-新游测试与公会争霸 All Rights Reserved.
友情链接