需要用到的头文件为:
#include <sys/types.h>
#include <dirent.h>
#打开文件夹
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
opendir()函数打开一个指定路径name的文件夹关联的流,并将该流以执行结果的方式返回给调用者。在默认情况下,该流指向文件夹下的第一个目录。
fdopendir()函数与opendir()是相同的,只是该函数接收的方式是文件描述符fd。fd可以通过执行函数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查看)。
返回的结果是这样的一个结构体:
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用于标识当前的文件夹类型,支持的类型有:
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 *是指在遍历过程中所遍历到的每一个子目录dirent,filter可以根据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三种方式遍历文件夹的实现。
> #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
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()的扩展,参数中dirfd与pathname的用法跟 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.
[原文地址](http://sodino.com/2015/03/09/c-directory-io/) http://sodino.com/2015/03/09/c-directory-io/