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

rob*_*bor 106 c++ filesystems

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

180*_*ION 96

在标准C++中,从技术上讲,没有办法做到这一点,因为标准C++没有目录概念.如果你想稍微扩展你的网络,你可能想看看使用Boost.FileSystem.这已被接受包含在TR2中,因此这为您提供了尽可能接近标准的最佳实施机会.

一个例子,直接来自网站:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 文件,而不是目录 (29认同)
  • 关于最新升级版本的更新:如果有人遇到这个答案,最新的提升包括一个方便类boost :: recursive_directory_iterator,因此不再需要使用显式递归调用编写上述循环.链接:http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/reference.html#Class-recursive_directory_iterator (22认同)
  • 这曾经是一个很好的答案,但现在 <filesystem> 是标准的,最好简单地使用 is (请参阅其他答案的示例)。 (9认同)
  • C++没有文件概念?那std :: fstream怎么样?还是fopen? (5认同)
  • VC++ 11在std :: tr2 :: sys命名空间下的<filesystem>头文件中具有相同的功能. (5认同)

Adi*_*vit 45

在带有"Filesystem TS"的C++ <filesystem>11/14中,标题和范围 - for你可以简单地这样做:

#include <filesystem>

using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
     std::cout << dirEntry << std::endl;
Run Code Online (Sandbox Code Playgroud)

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

  • 那为什么呢?比引入你不使用的东西更好更具体。 (7认同)
  • [<filesystem>](http://en.cppreference.com/w/cpp/filesystem)不再是TS.它是C++ 17的一部分.你应该相应地更新这个答案. (5认同)
  • 避免使用 `using`,而是使用 `namespace`。 (2认同)
  • Mac 用户请注意,这至少需要 OSX 10.15 (Catalina)。 (2认同)

Jor*_*ira 42

如果使用Win32 API,则可以使用FindFirstFileFindNextFile函数.

http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 写这个有多长时间?我认为将C++粘贴到python并在一行中完成它需要更少的时间. (17认同)
  • 这是一个很好的非递归解决方案(有时候很方便!). (2认同)
  • 顺便说一句,如果有人想稍微编辑程序以接受路径的命令行参数 argv[1] 而不是硬编码的参数(“F:\\cvsrepos”),则 main(int, char) 的签名会改变到 wmain(int, wchar_t) 像这样: int wmain(int argc, wchar_t *argv[]) (2认同)
  • 谢谢,但此功能不适用于 Cyrilic。有什么方法可以使它与西里尔字符一起使用,例如 -б、в、г 等? (2认同)

Mat*_*u G 31

使用新的基于C++ 11范围forBoost可以使它更简单:

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 无需提升.OP特别要求标准c ++. (4认同)

Ale*_*lex 23

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

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 此例程不是递归的. (4认同)
  • @TimCooper,当然不是,dirent 是 posix 特定的。 (2认同)
  • 实际上,如果您获得 Tony Ronkko 的用于 Visual C++ 的 dirent.h 端口,它*确实*在 VC++ 上工作。是福斯。我刚试过这个,它的工作原理。 (2认同)

mrv*_*nzo 10

除了上面提到的boost :: filesystem之外,您可能还想检查wxWidgets :: wxDirQt :: QDir.

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

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

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

下面是示例代码(取自这里展示了如何遍历所有子目录#例8-5).

#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;
}
Run Code Online (Sandbox Code Playgroud)


poo*_*a13 7

您可以使用std::filesystem::recursive_directory_iterator. 但请注意,这包括符号(软)链接。如果你想避免它们,你可以使用is_symlink. 用法示例:

size_t directory_size(const std::filesystem::path& directory)
{
    size_t size{ 0 };
    for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
    {
        if (entry.is_regular_file() && !entry.is_symlink())
        {
            size += entry.file_size();
        }
    }
    return size;
}
Run Code Online (Sandbox Code Playgroud)

  • 最后但并非最不重要的一点是,实际上比以前的答案更好。 (2认同)

Dik*_*rAz 6

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

#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;                                    
}
Run Code Online (Sandbox Code Playgroud)

  • 请问“它”是什么?不是有语法错误吗?你如何养活“结束”?(=我们怎么知道我们解析了所有目录?) (2认同)
  • @yO_ 你是对的,有一个错字,recursive_directory_iterator 的默认构造函数将构造一个“无效”迭代器,当你完成对 dir 的迭代后,它会变成“它”将变得无效并等于“结束” (2认同)

ndr*_*xie 6

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

#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif
Run Code Online (Sandbox Code Playgroud)

每个案例的代码取自/sf/answers/4713551/

#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.
Run Code Online (Sandbox Code Playgroud)


Mat*_*ten 5

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

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


lei*_*eif 5

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

  • https://github.com/six-k/dtreetrawl/blob/f7c1d320225ee754b96fef28bb0774a2c34b91b8/dtreetrawl.c#L473 有一个这样的例子。该代码还做了一些事情,但它对于“nftw()”的使用来说是一个很好的教程。 (2认同)

abh*_*ora 5

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

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

如果实现无法访问分层文件系统,或者它不提供必要的功能,则文件系统库设施可能不可用。如果底层文件系统不支持某些功能,则它们可能不可用(例如,FAT 文件系统缺少符号链接并禁止多个硬链接)。在这些情况下,必须报告错误。

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

@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 ::文件系统::路径,它可以被用来获得file extensionfilenameroot name

考虑下面的例子。我一直在使用Ubuntu并在终端上编译它使用

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

#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;
}
Run Code Online (Sandbox Code Playgroud)