当前位置:首页> 正文

关于文件系统:如何在标准C ++中递归地遍历每个文件/目录?

关于文件系统:如何在标准C ++中递归地遍历每个文件/目录?

How do you iterate through every file/directory recursively in standard C++?

您如何在标准C ++中递归地遍历每个文件/目录?


在标准C ++中,从技术上讲,由于标准C ++没有目录概念,因此无法执行此操作。如果您想稍微扩展一下网络,可以考虑使用Boost.FileSystem。 TR2中已包含此功能,因此,这为您提供了使实现尽可能接近标准的最佳机会。

直接从网站获取的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool find_file( const path & dir_path,         // in this directory,
                const std::string & file_name, // search for this name,
                path & path_found )            // placing path here if found
{
  if ( !exists( dir_path ) ) return false;
  directory_iterator end_itr; // default construction yields past-the-end
  for ( directory_iterator itr( dir_path );
        itr != end_itr;
        ++itr )
  {
    if ( is_directory(itr->status()) )
    {
      if ( find_file( itr->path(), file_name, path_found ) ) return true;
    }
    else if ( itr->leaf() == file_name ) // see below
    {
      path_found = itr->path();
      return true;
    }
  }
  return false;
}

使用C ++ 17,标头和range- for,您可以简单地执行以下操作:

1
2
3
4
5
6
#include <filesystem>

using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
     std::cout << dirEntry << std::endl;

从C ++ 17开始,std::filesystem是标准库的一部分,可以在标头中找到(不再是"实验性")。


如果使用Win32 API,则可以使用FindFirstFile和FindNextFile函数。

http://msdn.microsoft.com/zh-CN/library/aa365200(VS.85).aspx

对于目录的递归遍历,必须检查每个WIN32_FIND_DATA.dwFileAttributes,以检查FILE_ATTRIBUTE_DIRECTORY位是否已设置。如果该位置1,则可以使用该目录递归调用该函数。另外,您可以使用堆栈来提供与递归调用相同的效果,但避免在很长的路径树上出现堆栈溢出。

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
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        }

        do {
            if (wcscmp(ffd.cFileName, L"
.") != 0 &&
                wcscmp(ffd.cFileName, L"
..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.push(path + L"
\" + ffd.cFileName);
                }
                else {
                    files.push_back(path + L"
\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}

int main(int argc, char* argv[])
{
    vector<wstring> files;

    if (ListFiles(L"
F:\\cvsrepos", L"*", files)) {
        for (vector<wstring>::iterator it = files.begin();
             it != files.end();
             ++it) {
            wcout << it->c_str() << endl;
        }
    }
    return 0;
}

您可以使用基于for和Boost的新C ++ 11系列使其变得更加简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <boost/filesystem.hpp>

using namespace boost::filesystem;    
struct recursive_directory_range
{
    typedef recursive_directory_iterator iterator;
    recursive_directory_range(path p) : p_(p) {}

    iterator begin() { return recursive_directory_iterator(p_); }
    iterator end() { return recursive_directory_iterator(); }

    path p_;
};

for (auto it : recursive_directory_range(dir_path))
{
    std::cout << it << std::endl;
}

一种快速的解决方案是使用C的Dirent.h库。

维基百科的工作代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}

除了上面提到的boost :: filesystem,您可能还需要检查wxWidgets :: wxDir和Qt :: QDir。

wxWidgets和Qt都是开源的,跨平台的C ++框架。

wxDir提供了一种灵活的方式来使用Traverse()或更简单的GetAllFiles()函数递归遍历文件。同样,您也可以使用GetFirst()GetNext()函数实现遍历(我假设Traverse()和GetAllFiles()是最终使用GetFirst()和GetNext()函数的包装器)。

QDir提供对目录结构及其内容的访问。使用QDir遍历目录有几种方法。您可以使用QDirIterator :: Subdirectories标志实例化的QDirIterator遍历目录内容(包括子目录)。另一种方法是使用QDir的GetEntryList()函数并实现递归遍历。

这是示例代码(从此处#例8-5中获取),显示了如何遍历所有子目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <qapplication.h>
#include <qdir.h>
#include <iostream>

int main( int argc, char **argv )
{
    QApplication a( argc, argv );
    QDir currentDir = QDir::current();

    currentDir.setFilter( QDir::Dirs );
    QStringList entries = currentDir.entryList();
    for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry)
    {
         std::cout << *entry << std::endl;
    }
    return 0;
}


Boost :: filesystem提供了recursive_directory_iterator,这对于此任务非常方便:

1
2
3
4
5
6
7
8
9
#include"boost/filesystem.hpp"
#include <iostream>

using namespace boost::filesystem;

recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
    std::cout << *it << std::endl;                                    
}

您可以使用ftw(3)nftw(3)在POSIX系统上以C或C ++遍历文件系统层次结构。


使用boost或c ++ 14的实验性文件系统可能是最好的选择。如果要解析内部目录(即,在程序关闭后用于程序存储数据的目录),则创建一个索引文件,其中包含文件内容的索引。顺便说一句,将来可能需要使用boost,所以如果您没有安装,请安装它!其次,您可以使用条件编译,例如:

1
2
#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif

每种情况的代码均来自https://stackoverflow.com/a/67336/7077165

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
#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        }

        do {
            if (wcscmp(ffd.cFileName, L"
.") != 0 &&
                wcscmp(ffd.cFileName, L"
..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.push(path + L"
\" + ffd.cFileName);
                }
                else {
                    files.push_back(path + L"
\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}
#endif
//so on and so forth.

你不知道C ++标准没有目录的概念。将字符串转换为文件句柄取决于实现。该字符串的内容及其映射的内容取决于操作系统。请记住,可以使用C ++编写该OS,因此它的使用级别尚未定义询问如何遍历目录的权限(因为您正在编写目录管理代码)。

请查看您的OS API文档以了解如何执行此操作。如果您需要具有便携性,则必须为各种操作系统配备一堆#ifdef。


您需要调用操作系统特定的函数来进行文件系统遍历,例如open()readdir()。 C标准未指定任何与文件系统相关的功能。


我们在2019年。我们在C++中有文件系统标准库。 Filesystem library提供了对文件系统及其组件(例如路径,常规文件和目录)执行操作的便利。

如果您正在考虑可移植性问题,则在此链接上有一个重要说明。它说:

The filesystem library facilities may be unavailable if a hierarchical file system is not accessible to the implementation, or if it does not provide the necessary capabilities. Some features may not be available if they are not supported by the underlying file system (e.g. the FAT filesystem lacks symbolic links and forbids multiple hardlinks). In those cases, errors must be reported.

文件系统库最初是作为boost.filesystem开发的,已发布为技术规范ISO / IEC TS 18822:2015,并最终从C ++ 17合并为ISO C ++。目前,与C ++ 17库相比,boost实现在更多的编译器和平台上可用。

@ adi-shavit已成为std :: experimental的一部分时回答了这个问题,他于2017年更新了此答案。我想提供有关该库的更多详细信息,并显示更多详细的示例。

std :: filesystem :: recursive_directory_iterator是一个LegacyInputIterator,它遍历目录的directory_entry元素,并递归遍历所有子目录的条目。没有指定迭代顺序,只是每个目录条目仅被访问一次。

如果您不想递归遍历子目录的条目,则应使用directory_iterator。

两个迭代器都返回一个directory_entry对象。 directory_entry具有各种有用的成员函数,例如is_regular_fileis_directoryis_socketis_symlink等。path()成员函数返回std :: filesystem :: path对象,可用于获取file extensionfilenameroot name

考虑下面的示例。我一直在使用Ubuntu并使用以下命令在终端上对其进行编译

g ++ example.cpp --std = c ++ 17 -lstdc ++ fs -Wall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
#include <filesystem>

void listFiles(std::string path)
{
    for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
        if (!dirEntry.is_regular_file()) {
            std::cout <<"Directory:" << dirEntry.path() << std::endl;
            continue;
        }
        std::filesystem::path file = dirEntry.path();
        std::cout <<"Filename:" << file.filename() <<" extension:" << file.extension() << std::endl;

    }
}

int main()
{
    listFiles("./");
    return 0;
}

你不知道标准C ++没有公开目录的概念。具体来说,它无法列出目录中的所有文件。

一个可怕的办法是使用system()调用并解析结果。最合理的解决方案是使用某种跨平台库,例如Qt甚至POSIX。


如果您使用的是Windows,则可以将FindFirstFile和FindNextFile API一起使用。您可以使用FindFileData.dwFileAttributes来检查给定路径是文件还是目录。如果是目录,则可以递归地重复该算法。

在这里,我整理了一些代码,列出了Windows计算机上的所有文件。

http://dreams-soft.com/projects/traverse-directory


展开全文阅读

相关内容