LinuxC讲解系统调⽤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(参见)
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 typec转dp
regular file 292872 9057525276248794967 64 37 RFC959_FTP传输协议(中⽂版).pdf regular file 288738 9223372036854775807 40 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\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(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
regular file 292872 9057525276248794967 64 37 RFC959_FTP传输协议(中⽂版).pdf
regular file 288738 9223372036854775807 40 pdf
exit list_file_r
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论