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

有帮助吗?

解决方案

在标准 C++ 中,从技术上讲,没有办法做到这一点,因为标准 C++ 没有目录的概念。如果你想稍微扩展一下你的网络,你可能想看看使用 升压文件系统. 。这已被接受并包含在 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;
}

其他提示

如果使用 Win32 API,您可以使用 查找第一个文件查找下一个文件 功能。

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

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

#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;
}

您可以使用新功能使其变得更加简单 C++11 基于范围 for促进:

#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;
}

在带有“文件系统 TS”的 C++11/14 中, <experimental/filesystem> 标头和范围-for 你可以简单地这样做:

#include <experimental/filesystem>

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

从 C++17 开始, std::filesystem 是标准库的一部分,可以在 <filesystem> 标题(不再是“实验性的”)。

一个快速的解决方案是使用 C 迪伦特.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;
}

除了上面提到的 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;
}

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;                                    
}

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

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

查看操作系统 API 文档以了解如何执行此操作。如果你需要便携,你就必须有一堆 #ifdef适用于各种操作系统。

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

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

每个案例的代码取自 https://stackoverflow.com/a/67336/7077165

#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.

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

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

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

我们已经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_entry 有各种有用的成员函数,例如 is_regular_file, is_directory, is_socket, is_symlink ETC。这 path() 成员函数返回一个对象 std::文件系统::路径 它可以用来得到 file extension, filename, root 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;
}

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top