本文内容如下:

需要用到的头文件为:
#include <sys/types.h>
#include <dirent.h>


#打开文件夹

DIR *opendir(const char *name);
DIR *fdopendir(int fd);

opendir()函数打开一个指定路径name的文件夹关联的流,并将该流以执行结果的方式返回给调用者。在默认情况下,该流指向文件夹下的第一个目录。
fdopendir()函数与opendir()是相同的,只是该函数接收的方式是文件描述符fdfd可以通过执行函数int dirfd(DIR *dirp);获得。需要注意的是将fd做为参数传递给fdopendir()之后,则该fd就不要在程序中的其它地方使用了。
以上两个函数正常执行时都会返回与文件夹相关联的流;否则返回NULL,错误的原因可以从errno中获得。


#遍历文件夹下的内容

#####读取文件夹下的子文件(夹)

struct dirent *readdir(DIR *dirp); NO Thread-Safe
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); Thread-Safe

执行readdir()函数将返回文件夹流dirp中的下一个条目,即文件夹下的子目录,重复执行时将会逐一返回文件夹下所有的子目录;一旦返回NULL则表示已经把所有的子目录都遍历过了或者出现了错误(可以通过errno查看)。
返回的结果是这样的一个结构体:

1
2
3
4
5
6
7
struct dirent {
ino_t d_ino; /* 在文件系统中的inode number */
off_t d_seekoff; /* 与文件夹流的位置指针操作相关 */
unsigned short d_reclen; /* 本记录的数据长度 */
unsigned char d_type; /* 当前遍历子项的文件类型:文件、文件夹、link、socket等 */
char d_name[256]; /* 当前遍历子项的文件名 */
};

以上结构体出自 man ,在不同系统上会有各自不同的差别。

在上面的结构体中,文件名d_name的限制长度256是包括字符串终止符’\0’的,所以在OS中,文件夹的命名最大字符数是255个。(在Mac中尝试是无法输入第256个字符的)
结构体中的d_type用于标识当前的文件夹类型,支持的类型有:

1
2
3
4
5
6
7
8
DT_BLK This is a block device.
DT_CHR This is a character device.
DT_DIR 文件夹 This is a directory.
DT_FIFO This is a named pipe (FIFO).
DT_LNK 符号链接文件 This is a symbolic link.
DT_REG 文件 This is a regular file.
DT_SOCK SOCKET链接 This is a UNIX domain socket.
DT_UNKNOWN The file type is unknown.

readdir_r()readdir()的可重入实现版本,功能是一致的,但readdir_r()是线程安全的。
执行readdir_r()时遍历得到的子目录数据会存放在第二个参数entry中,第三个参数result指针指向子目录的entry,且函数返回值返回0。当遍历到文件夹结束的位置时,返回值仍返回0,但第三个参数result指针指向NULL。当函数执行错误时,则返回一个大于0的数字(即EBADF)。

#####文件夹位置指针的操作

long telldir(DIR *dirp);
void seekdir(DIR *dirp, long loc);
void rewinddir(DIR *dirp);

DIR也是流的一种,对其进行读操作时也有位置指针的概念及操作。
telldir()可以获取当前的位置指针位置。返回值表示当前遍历的子目录是当前文件夹下的第几个目录(第0个是”.”当前目录,第1个是”..”上一级目录,所以从第2个开始计数。)
seekdir()可以操作位置指针跳转到指定的子目录序号loc
rewinddir()可以将位置指针恢复到流的起始位置。

#####遍历、过滤文件夹下的子文件(夹)

int scandir(const char *dirp, struct dirent ***namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));

int scandirat(int dirfd, const char *dirp, struct dirent ***namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));

scandir()是遍历文件夹的另一种方式。本函数支持自定义遍历的过滤条件及结果排序,封闭了遍历的实现,执行后直接提供合法的目录信息,但该信息也只是目录名称而已。函数的返回结果是合法的子目录的个数;当函数执行出现错误时则返回-1且可以通过errno去错误详情。
dirp指定要遍历的文件夹路径。
namelist用于指向遍历结果。可以看出该指针指向一个二维的字符数组,该二维数组存储着文件夹下的所有合法的文件名。
*filter是一个函数指针,该函数有一个参数const struct dirent *是指在遍历过程中所遍历到的每一个子目录direntfilter可以根据dirent的类型、名称等信息来判定当前的dirent是否为合法的子目录,合法则函数返回0,则该子目录的名称会被存储在namelist中;否则返回非0,则该子目录被过滤掉。
*compar也是一个函数指针,该函数的两个参数都是const struct dirent *用于决定两个子目录在namelist中的顺序。

C的库中也默认提供了两个文件名称排序的方法:

int alphasort(const struct dirent **a, const struct dirent **b);
int versionsort(const struct dirent **a, const struct dirent **b);

alphasort()的实现是把dirent的名称用strcoll()进行比较,排序的结果是按ASCII编码的值由小到大排序。
versionsort()的实现是把dirent的名称strverscmp()进行比较,排序的结果是也按ASCII编码的值由小到大排序,不同的是支持对名称中按数字序号的排序。举个例子对比alphasort()versionsort()的不同:

alphasort: pc1 pc10 pc2 … pc9
versionsort: pc1 pc2 … pc9 pc10

很明显,versionsort()的排序结果更符合普通人的阅读习惯。

scandirat()scandirat()的扩展函数,主要区别就在于第一个参数dirfd与第二个参数dirp
如果dirp是一个绝对路径的字符串,则函数无视参数dirfd,功能与scandir()一致。
如果dirp是一个相对路径的字符串,且参数dirfd的值是AT_FDCWD (#include <fcntl.h>),则表示从应用程序当前的工作路径拼接上dirp的相对路径所组成的绝对路径下去遍历;当dirfd值不为AT_FDCWD时而是一个代替文件夹的file descriptor(参考dirfd(DIR*))时,则遍历的文件夹路径即为dirfd所指向的文件夹再拼接上dirp的相对路径所组成的绝对路径。

下面的示例代码演示了readdir readdir_r scandir三种方式遍历文件夹的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
static void printFileType(int type) {
// 子文件 类型
printf("type:%x ", type);
switch (type) {
case DT_FIFO:
printf(" fifo.");
break;
case DT_CHR:
printf(" character.");
break;
case DT_DIR:
printf(" directory.");
break;
case DT_BLK:
printf(" block.");
break;
case DT_REG:
printf(" regular file.");
break;
case DT_LNK:
printf(" link.");
break;
case DT_SOCK:
printf(" socket.");
break;
case DT_WHT:
printf(" WHT.");
break;
case DT_UNKNOWN:
default:
printf(" unknown.");
break;
}
}
static void print_dir(const char * dirPath, DIR * dir) {
struct dirent * file;
// 遍历文件夹下的内容
while ((file = readdir(dir)) != NULL) {
printf("dir position=%ld ", telldir(dir));
// 排队掉"."(当前目录)和".."(上一级),无用的信息
if(strcmp(file->d_name, ".") == 0
|| strcmp(file->d_name, "..") == 0) {
printf("ignore:%s \n", file->d_name);
continue;
}
// 子文件 文件名
printf("sub file:%20s ", file->d_name);
// 子文件 inode节点信息
printf("inode:%llx ", file->d_ino);
printf("off:%llx ", file->d_seekoff); // 和流的位置指针相关
printFileType(file->d_type);
printf("\n");
}
}
static void print_dir_r(DIR * dir) {
struct dirent entry;
struct dirent * result = &entry;
while(1){
printf("dir position=%ld ", telldir(dir));
int res = readdir_r(dir, result, &result);
if (res == 0){
if (result == NULL) {
break;
} else {
printf("sub file:%20s ", entry.d_name);
printFileType(entry.d_type);
printf("\n");
}
} else {
printf("readdir_r() fail:%s \n", strerror(errno));
break;
}
}
printf("\n");
}
/**过滤掉的“.”与".."*/
static int filterDot(const struct dirent * dir) {
if (strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name, "..") == 0) {
// 过滤掉 "."和".."
return 0;
} else {
return 1;
}
}
static void print_scan_dir(const char * path) {
// 指向字符的二维数组,该数组存储着文件夹下的合法子目录名
struct dirent **namelist;
int n;
n = scandir(path, &namelist, filterDot, alphasort);
printf("after scandir, entry num:%d \n", n);
if (n < 0) {
perror("scandir");
} else {
int idx = 0;
while (idx !=n) {
printf("idx=%d suf file:%20s \n", idx, namelist[idx]->d_name);
// 释放掉namelist
free(namelist[idx]);
idx ++;
}
// 释放掉namelist
free(namelist);
}
}
int main(int argc, char ** argv) {
// 指定的一个文件夹路径
char * dirPath = "/Users/sodino/workspace/xcode/CString/CString/test";
// 打开文件夹
DIR* dir = opendir(dirPath);
print_dir(dirPath, dir);
// 重置文件夹位置指针至流的起始位置,以便后续再次遍历
rewinddir(dir);
printf("\n\ndo rewinddir() and go on to list entry(s)\n\n");
print_dir_r(dir);
// 重置文件夹位置指针至流的起始位置,以便后续再次遍历
rewinddir(dir);
printf("\n\n do scandir() \n");
print_scan_dir(dirPath);
// 关闭
closedir(dir);
return EXIT_SUCCESS;
}

效果如下图:
directory


#文件夹关闭

int closedir(DIR *dirp);

closedir()将关闭一个与文件夹相关联的流。执行本函数后,dirp将不可再被使用。


#删除文件夹

int rmdir(const char *pathname);
删除指定路径下的文件夹。如果删除成功,则返回0;否则返回-1,并可通过errno查看失败原因。


#创建文件夹

int mkdir(const char *pathname, mode_t mode);
int mkdirat(int dirfd, const char *pathname, mode_t mode);

mkdir()函数用于创建文件夹,参数pathname指定要创建的文件夹的绝对路径。参数mode用于限定文件夹的文件权限(组)。该mode将会OR操作后合成struct stat中的st_mode(详情)。文件权限(组)的mode具体定义见下文的File Mode Bits(懒得翻译了,直接上英文)。所设置的文件权限(组)可以通过命令ls -l -a查看。
如果函数正常执行并创建文件夹成功,则返回0;否则返回-1并且可以通过errno查看错误详情。

扩展:直接mkdir(path, S_IRWXU| S_IRWXG| S_IRWXO),可生成的文件夹权限组是drwxr-xr-x而不是drwxrwxrwx?

原因是:mkdir()函数中指定的参数mode值会和当前程序的umask处理一下,具体处理为mode & ~unmask & 0777才是最终的文件夹权限组。查看当前用户或程序的umask可以在命令行终端上输入命令umask可看。一般用户的umask值为0022

mkdirat()函数是mkdir()的扩展,参数中dirfdpathname的用法跟 scandir()scandirat()的用法是一致的,这里就不重复说明了。

#####File Mode Bits:#####

S_IRWXU read, write, execute/search by owner
S_IRUSR read permission, owner
S_IWUSR write permission, owner
S_IXUSR execute/search permission, owner
S_IRWXG read, write, execute/search by group
S_IRGRP read permission, group
S_IWGRP write permission, group
S_IXGRP execute/search permission, group
S_IRWXO read, write, execute/search by others
S_IROTH read permission, others
S_IWOTH write permission, others
S_IXOTH execute/search permission, others
S_ISUID set-user-ID on execution
S_ISGID set-group-ID on execution
S_ISVTX on directories, restricted deletion flag

The bits defined by S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, S_ISUID, S_ISGID and S_ISVTX are unique.
S_IRWXU is the bitwise OR of S_IRUSR, S_IWUSR and S_IXUSR.
S_IRWXG is the bitwise OR of S_IRGRP, S_IWGRP and S_IXGRP.
S_IRWXO is the bitwise OR of S_IROTH, S_IWOTH and S_IXOTH.