it-swarm.cn

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

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

97
robottobor

在标准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;
}
92
1800 INFORMATION

如果使用Win32 API,您可以使用 FindFirstFile FindNextFile functions。

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

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

#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;
}
40
Jorge Ferreira

使用新的 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;
}
30
Matthieu G

在带有“Filesystem TS”的C++ 11/14中, <experimental/filesystem> header和range -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>标题中找到(不再是“实验性的”)。

24
Adi Shavit

一个快速的解决方案是使用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;
}
22
Alex

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

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

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

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

下面是示例代码(取自 here #Example 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;
}
9
mrvincenzo

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

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

查看您的OS API文档以了解如何执行此操作。如果你需要便携式,那么你必须拥有一堆 #ifdef s用于各种操作系统。

3
Matthew Scouten

您可以使用 ftw(3)nftw(3)_ posix _ systems上使用C或C++中的文件系统层次结构。

3
leif

您需要为文件系统遍历调用特定于OS的函数,如open()readdir()。 C标准没有指定任何与文件系统相关的功能。

2
John Millikin

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

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

代码在 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.
2
ndrewxie

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

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

1
shoosh