代码之家  ›  专栏  ›  技术社区  ›  robottobor

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

  •  168
  • robottobor  · 技术社区  · 16 年前

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

    19 回复  |  直到 16 年前
        1
  •  91
  •   1800 INFORMATION    16 年前

    从C++17开始 <filesystem> 标题和范围- 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;
    

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

        2
  •  38
  •   ChrisN    16 年前

    在标准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;
    }
    
        3
  •  30
  •   Peter Mortensen icecrime    11 年前

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

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

    对于目录的递归遍历,您必须检查每个目录 WIN32_FIND_DATA.dw文件属性 检查是否 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;
    }
    
        4
  •  22
  •   Peter Mortensen icecrime    11 年前

    你可以用新的让它更简单 C++11 基于范围 for Boost :

    #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;
    }
    
        5
  •  21
  •   Rakete1111    6 年前

    一个快速的解决方案是使用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;
    }
    
        6
  •  9
  •   tonymontana    13 年前

    您可以使用 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;
    }
    
        7
  •  5
  •   DikobrAz    7 年前

    除了上述boost::文件系统之外,您可能还需要检查 wxWidgets::wxDir Qt::QDir .

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

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

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

    以下是示例代码(取自 here #示例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;
    }
    
        8
  •  3
  •   Peter Mortensen icecrime    11 年前

    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;                                    
    }
    
        9
  •  3
  •   Peter Mortensen icecrime    11 年前

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

        10
  •  2
  •   John Millikin    16 年前

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

    有一个重要的注意事项 this link 如果你正在考虑可移植性问题。上面写着:

    如果实现无法访问分层文件系统,或者如果它不提供必要的功能,则文件系统库设施可能不可用。如果底层文件系统不支持某些功能,则这些功能可能不可用(例如,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_file , is_directory , is_socket , is_symlink 等等这个 path() 成员函数返回一个对象 std::filesystem::path 它可以用来获得 file extension , filename , root name .

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

    g++示例.cpp--标准=c++17-lstdc++fs-墙

    #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;
    }
    
        11
  •  2
  •   ndrewxie    7 年前

    您可能最适合使用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.
    
        12
  •  1
  •   Peter Mortensen icecrime    11 年前

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

    查看您的操作系统API文档,了解如何做到这一点。如果你需要便携,你必须有一堆 #ifdef 适用于各种操作系统。

        13
  •  0
  •   Ibrahim    11 年前

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