Linux下的dirent.h与C++17的std::filesystem命名空间
实例
方法一:UNIX下的dirent.h
例子1:列出一个目录中所有文件的名字,下面代码是ls -aR
命令的简要实现。
我们包含一个系统头文件dirent.h
,以便使用opendir
和readdir
的函数原型,以及dirent
结构的定义。
因为各种不同UNIX系统目录项的实际格式是不一样的,所以使用函数opendir
、readdir
和closedir
对目录进行处理。
opendir
函数返回指向DIR结构的指针,我们将指针传送给readdir
函数。我们并不关心DIR结构中包含了什么。然后,在循环中调用readdir
来读每个目录项。它返回一个指向dirent
结构的指针,而当目录中已无目录项可读时则返回null指针。在dirent
结构中取出的只是每个目录项的名字(d_name
)。使用该名字,此后就可以调用stat
函数以获得该文件的所有属性。
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
#include <unistd.h>
#include <cstdio>
#include <dirent.h>
#include <cstring>
#include <sys/stat.h>
#include <cstdlib>
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if ((dp = opendir(dir)) == NULL){
fprintf(stderr, "Can't open directory %s\n", dir);
return ;
}
chdir(dir);
while((entry = readdir(dp)) != NULL){
lstat(entry->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)){
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
printf("%*s%s\n", depth, "", entry->d_name);
printdir(entry->d_name, depth+4);;
}else
printf("%*s%s\n", depth, "", entry->d_name);
}
chdir("..");
closedir(dp);
}
int main(int argc, char *argv[])
{
char *topdir = ".";
if (argc >= 2)
topdir = argv[1];
printf("Directory scan of %s\n", topdir);
printdir(topdir, 0);
printf("done.\n");
exit(0);
}
例子2:在小而简单的任务中,使用dirent.h。它可用作 UNIX 中的标准头文件,也可通过 Toni Ronkko 创建的兼容层 用于Windows 。以下代码的功能类似于ls -a
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <cstdio>
#include <dirent.h>
#include <cstdlib>
int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *ent;
if ((dir = opendir("/root")) != NULL){
/* 打印目录中的所有文件和目录 */
while((ent = readdir(dir)) != NULL){
printf("%s\n", ent->d_name);
}
closedir(dir);
}else{
/* 无法打开目录 */
perror("");
return EXIT_FAILURE;
}
}
获取目录中的文件列表的算法:
1
2
3
4
5
6
7
8
9
10
开始
声明一个指向 DIR 类型的指针 dr。
声明另一个指向 dirent 结构的指针 en。
调用 opendir() 函数打开当前目录下的所有文件。
将 dr 指针初始化为 dr = opendir(".")。
if(dr)
while ((en = readdir(dr)) != NULL)
使用 en->d_name 打印所有文件名。
调用 closedir() 函数关闭目录。
结尾。
1
2
3
4
5
6
7
8
9
10
Begin
Declare a poniter dr to the DIR type.
Declare another pointer en of the dirent structure.
Call opendir() function to open all file in present directory.
Initialize dr pointer as dr = opendir(".").
If(dr)
while ((en = readdir(dr)) != NULL)
print all the file name using en->d_name.
call closedir() function to close the directory.
End.
例子3:
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
/*
* 演示基本目录列表的示例。
*
* 使用 Visual Studio 编译此文件,并在控制台中使用目录名称参数运行生成的命令。 例如,命令
*
* ls "c:\Program Files"
*
* 可能会输出类似的东西
*
* ./
* ../
* 7-Zip/
* Internet Explorer/
* Microsoft Visual Studio 9.0/
* Microsoft.NET/
* Mozilla Firefox/
*
* 该文件提供的ls命令只是一个例子:该命令在Linux中没有像“ls -al”这样的花哨选项,并且该命令不支持像“ls *.c”这样的文件名匹配。
*
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <locale.h>
static void list_directory(const char* dirname);
int main(int argc, char* argv[])
{
/* 选择默认语言环境 */
setlocale(LC_ALL, "LC_CTYPE=.utf8");
/* 对于命令行中的每个目录 */
int i = 1;
while (i < argc)
{
list_directory(argv[i]);
i++;
}
/* 如果命令行上没有参数,则列出当前工作目录 */
if (argc == 1)
{
list_directory(".");
}
return EXIT_SUCCESS;
}
/* 列出目录中的文件和目录 */
static void list_directory(const char* dirname)
{
/*打开目录流 */
DIR* dir = opendir(dirname);
if (!dir)
{
/* 无法打开目录 */
fprintf(stderr, "Cannot open %s (%s)\n", dirname, strerror(errno));
exit(EXIT_FAILURE);
}
/* 打印目录中的所有文件和目录 */
struct dirent* ent;
while ((ent = readdir(dir)) != NULL)
{
switch (ent->d_type) {
case DT_REG:
printf("%s\n", ent->d_name);
break;
case DT_DIR:
printf("%s\n", ent->d_name);
break;
case DT_LNK:
printf("%s@\n", ent->d_name);
break;
default:
printf("%s*\n", ent->d_name);
}
}
closedir(dir);
}
方法二:跨平台,C++ 17的std::filesystem
列出文件系统的文件
在 C++17 中,现在有一种正式的方法来列出文件系统的文件:std::filesystem::directory_iterator
。此外,std::filesystem::recursive_directory_iterator
也可以迭代子目录。
注意:
需要在Visual Studio中启用C++ 17编译,在项目>属性>C/C++语言>C++语言标准
下,有一些可用选项:
- ISO C++14 标准。msvc 命令行选项:
/std:c++14
- ISO C++17 标准。msvc 命令行选项:
/std:c++17
- ISO C++20 标准。msvc 命令行选项:
/std:c++20
- 最新的标准草案。msvc 命令行选项:
/std:c++latest
1
2
3
4
5
6
7
8
9
10
#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
std::string path = "C:\\Develop";
for (const auto& entry : fs::directory_iterator(path))
std::cout << entry.path() << std::endl;
}
如果你使用CLion,需要将CMakeLists.txt中的set(CMAKE_CXX_STANDARD 14)
修改为set(CMAKE_CXX_STANDARD 17)
:
cmake_minimum_required(VERSION 3.20)
project(untitled1)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled1 main.cpp)
dirent.h详解
opendir
名字
opendir - 打开一个目录
概要
1
2
3
4
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *dirname)
描述
opendir()
函数打开与由 dirname
参数命名的目录相对应的目录流。 目录流位于第一个条目。 如果使用文件描述符实现 DIR
类型,则应用程序最多只能打开 {OPEN_MAX}
个文件和目录。 对任何 exec
函数的成功调用将关闭在调用过程中打开的任何目录流。
返回值
成功完成后,opendir()
返回一个指向 DIR
类型对象的指针。 否则,返回空指针并设置 errno
以指示错误。
错误
如果出现以下情况,opendir()
函数将失败:
[EACCES]
dirname
路径前缀部分的搜索权限被拒绝,或者dirname
的读取权限被拒绝。
[ELOOP]
解析路径时遇到太多符号链接。
[ENAMETOOLONG]
dirname 参数的长度超过 {PATH_MAX}
,或者路径名组件长于 {NAME_MAX}
。
[ENOENT]
dirname
的组件未命名现有目录或 dirname
是空字符串。
[ENOTDIR]
dirname
的组成部分不是目录。
如果出现以下情况,opendir()
函数可能会失败:
[EMFILE]
{OPEN_MAX}
个文件描述符当前在调用进程中打开。
[ENAMETOOLONG]
符号链接的路径名解析产生了长度超过 {PATH_MAX}
的中间结果。
[ENFILE]
系统中当前打开的文件过多。
readdir
名字
readdir, readdir_r - 读取目录
概要
1
2
3
4
5
#include <sys/types.h>
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
描述
DIR
类型定义在头部 <dirent.h>
中,表示目录流,它是特定目录中所有目录条目的有序序列。目录条目代表文件;文件可以从目录中删除或添加到目录中,以与 readdir()
的操作异步。
readdir()
函数返回一个指向结构的指针,该结构表示由参数 dirp
指定的目录流中当前位置的目录条目,并将目录流定位在下一个条目。它在到达目录流的末尾时返回一个空指针。 <dirent.h>
头文件定义的结构 dirent
描述了一个目录条目。
如果存在dot
或dot-dot
条目,dot
返回1个条目,dot-dot
返回1个条目;否则他们将不会被退回。
readdir()
返回的指针指向的数据可能会被同一目录流上的另一个readdir()
调用覆盖。此数据不会被另一个目录流上的readdir()
调用覆盖。
如果在最近一次调用 opendir()
或 rewinddir()
之后从目录中删除或添加文件,则未指定对 readdir()
的后续调用是否返回该文件的条目。
readdir()
函数可能会在每次实际读取操作时缓冲多个目录条目; readdir()
标记每次实际读取目录时更新目录的 st_atime
字段。
在调用 fork()
之后,父进程或子进程(但不是两者)可以继续使用 readdir()
、rewinddir()
或 seekdir()
处理目录流。如果父进程和子进程都使用这些函数,则结果未定义。
如果条目命名一个符号链接,则 d_ino
成员的值是未指定的。
readdir()
接口不需要可重入。
readdir_r()
函数初始化entry
引用的dirent
结构,表示dirp
引用的目录流中当前位置的目录条目,在result
引用的位置存储指向该结构的指针,并将目录流定位在dirp
引用的位置下一个条目。
entry
指向的存储空间对于具有至少包含{NAME_MAX}
和一个元素的 char d_name
成员数组的目录来说足够大。
成功返回时,在 *result
处返回的指针将具有与参数条目相同的值。在到达目录流的末尾时,该指针的值为 NULL
。
readdir_r()
函数不会返回包含空名称的目录条目。未指定是否为点或点点返回条目。
如果在最近一次调用 opendir()
或 rewinddir()
之后从目录中删除或添加文件,则未指定对 readdir_r()
的后续调用是否返回该文件的条目。
readdir_r()
函数可能会在每次实际读取操作时缓冲多个目录条目; readdir_r()
函数标记每次实际读取目录时更新目录的 st_atime
字段。
希望检查错误情况的应用程序应在调用 readdir()
之前将 errno
设置为 0。如果 errno
在返回时设置为非零,则发生错误。
返回值
成功完成后, readdir()
返回一个指向 struct dirent
类型对象的指针。 当遇到错误时,返回空指针并设置 errno
以指示错误。 当遇到目录末尾时,返回一个空指针并且 errno
不变。
如果成功,则 readdir_r()
函数返回零。 否则,返回错误编号以指示错误。
错误
在以下情况下 readdir()
函数将失败:
[EOVERFLOW]
要返回的结构中的值之一无法正确表示。
在以下情况下 readdir()
函数可能会失败:
[EBADF]
dirp
参数不引用打开的目录流。
[ENOENT]
目录流的当前位置无效。
如果出现以下情况, readdir_r()
函数可能会失败:
[EBADF]
dirp
参数不引用打开的目录流。
例子
以下示例代码将在当前目录中搜索条目名称:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dirp = opendir(".");
while (dirp) {
errno = 0;
if ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, name) == 0) {
closedir(dirp);
return FOUND;
}
} else {
if (errno == 0) {
closedir(dirp);
return NOT_FOUND;
}
closedir(dirp);
return READ_ERROR;
}
}
return OPEN_ERROR;
closedir
名字
closedir - 关闭目录流
概要
1
2
3
4
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
描述
closedir()
函数关闭由参数 dirp
引用的目录流。 返回时,dirp
的值可能不再指向 DIR
类型的可访问对象。 如果文件描述符用于实现类型 DIR
,则该文件描述符将被关闭。
返回值
成功完成后, closedir()
返回 0。否则,返回 -1 并设置 errno
以指示错误。
错误
如果出现以下情况,closedir()
函数可能会失败:
[EBADF]
dirp
参数不引用打开的目录流。
[EINTR]
closedir()
函数被信号中断。
dirent.h
名字
dirent.h - 目录条目的格式
概要
1
#include <dirent.h>
描述
目录的内部格式未指定。
<dirent.h>
头文件通过 typedef
定义了以下数据类型:
DIR
表示目录流的类型。
它还定义了结构 dirent
,其中包括以下成员:
1
2
ino_t d_ino 文件序列号
char d_name[] 条目名称
ino_t
类型的定义如 <sys/types.h>
中所述。
字符数组d_name
的大小未指定,但终止空字节之前的字节数不会超过 {NAME_MAX}
。
以下声明为函数,也可以定义为宏。 必须提供函数原型以供 ISO C 编译器使用。
1
2
3
4
5
6
7
int closedir(DIR *);
DIR *opendir(const char *);
struct dirent *readdir(DIR *);
int readdir_r(DIR *, struct dirent *, struct dirent **);
void rewinddir(DIR *);
void seekdir(DIR *, long int);
long int telldir(DIR *);
参考
How can I get the list of files in a directory using C or C++?
c++17 filesystem is not a namespace-name
How to enable C++17 compiling in Visual Studio?