如何在标准 C++ 中递归遍历每个文件/目录?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/67273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you iterate through every file/directory recursively in standard C++?
提问by robottobor
How do you iterate through every file/directory recursively in standard C++?
如何在标准 C++ 中递归遍历每个文件/目录?
采纳答案by 1800 INFORMATION
In standard C++, technically there is no way to do this since standard C++ has no conception of directories. If you want to expand your net a little bit, you might like to look at using Boost.FileSystem. This has been accepted for inclusion in TR2, so this gives you the best chance of keeping your implementation as close as possible to the standard.
在标准 C++ 中,技术上没有办法做到这一点,因为标准 C++ 没有目录的概念。如果你想稍微扩展你的网络,你可能想看看使用Boost.FileSystem。这已被接受以包含在 TR2 中,因此这为您提供了使您的实现尽可能接近标准的最佳机会。
An example, taken straight from the website:
一个例子,直接取自网站:
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;
}
回答by Adi Shavit
From C++17 onward, the <filesystem>
header, and range-for
, you can simply do this:
从 C++17 开始,<filesystem>
标题和 range- 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;
As of C++17, std::filesystem
is part of the standard library and can be found in the <filesystem>
header (no longer "experimental").
从 C++17 开始,它std::filesystem
是标准库的一部分,可以在<filesystem>
头文件中找到(不再是“实验性的”)。
回答by Jorge Ferreira
If using the Win32 API you can use the FindFirstFileand FindNextFilefunctions.
如果使用 Win32 API,您可以使用FindFirstFile和FindNextFile函数。
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
For recursive traversal of directories you must inspect each WIN32_FIND_DATA.dwFileAttributesto check if the FILE_ATTRIBUTE_DIRECTORYbit is set. If the bit is set then you can recursively call the function with that directory. Alternatively you can use a stack for providing the same effect of a recursive call but avoiding stack overflow for very long path trees.
对于目录的递归遍历,您必须检查每个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;
}
回答by Matthieu G
You can make it even simpler with the new C++11range based for
and Boost:
您可以使用新的基于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;
}
回答by Alex
A fast solution is using C's Dirent.hlibrary.
一个快速的解决方案是使用 C 的Dirent.h库。
Working code fragment from Wikipedia:
来自维基百科的工作代码片段:
#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;
}
回答by mrvincenzo
In addition to the above mentioned boost::filesystem you may want to examine wxWidgets::wxDirand Qt::QDir.
除了上面提到的 boost::filesystem 之外,您可能还想检查wxWidgets::wxDir和Qt::QDir。
Both wxWidgets and Qt are open source, cross platform C++ frameworks.
wxWidgets 和 Qt 都是开源的、跨平台的 C++ 框架。
wxDir
provides a flexible way to traverse files recursively using Traverse()
or a simpler GetAllFiles()
function. As well you can implement the traversal with GetFirst()
and GetNext()
functions (I assume that Traverse() and GetAllFiles() are wrappers that eventually use GetFirst() and GetNext() functions).
wxDir
提供了一种灵活的方式来使用Traverse()
或更简单的GetAllFiles()
函数递归地遍历文件。您也可以使用GetFirst()
和GetNext()
函数实现遍历(我假设 Traverse() 和 GetAllFiles() 是最终使用 GetFirst() 和 GetNext() 函数的包装器)。
QDir
provides access to directory structures and their contents. There are several ways to traverse directories with QDir. You can iterate over the directory contents (including sub-directories) with QDirIterator that was instantiated with QDirIterator::Subdirectories flag. Another way is to use QDir's GetEntryList() function and implement a recursive traversal.
QDir
提供对目录结构及其内容的访问。有几种方法可以使用 QDir 遍历目录。您可以使用使用 QDirIterator::Subdirectories 标志实例化的 QDirIterator 迭代目录内容(包括子目录)。另一种方法是使用 QDir 的 GetEntryList() 函数并实现递归遍历。
Here is sample code (taken from here# Example 8-5) that shows how to iterate over all sub directories.
这是示例代码(取自此处#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;
}
回答by DikobrAz
Boost::filesystem provides recursive_directory_iterator, which is quite convenient for this task:
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;
}
回答by leif
You can use ftw(3)
or nftw(3)
to walk a filesystem hierarchy in C or C++ on POSIXsystems.
您可以在POSIX系统上使用ftw(3)
或nftw(3)
在 C 或 C++ 中遍历文件系统层次结构。
回答by ndrewxie
You would probably be best with either boost or c++14's experimental filesystem stuff. IFyou are parsing an internal directory (ie. used for your program to store data after the program was closed), then make an index file that has an index of the file contents. By the way, you probably would need to use boost in the future, so if you don't have it installed, install it! Second of all, you could use a conditional compilation, e.g.:
您可能最适合使用 boost 或 c++14 的实验性文件系统内容。如果您正在解析内部目录(即用于您的程序在程序关闭后存储数据),则创建一个索引文件,其中包含文件内容的索引。顺便说一句,您将来可能需要使用 boost,所以如果您没有安装它,请安装它!其次,您可以使用条件编译,例如:
#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif
The code for each case is taken from https://stackoverflow.com/a/67336/7077165
每个案例的代码取自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.
回答by Matthew Scouten
You don't. The C++ standard has no concept of directories. It is up to the implementation to turn a string into a file handle. The contents of that string and what it maps to is OS dependent. Keep in mind that C++ can be used to write that OS, so it gets used at a level where asking how to iterate through a directory is not yet defined (because you are writing the directory management code).
你没有。C++ 标准没有目录的概念。将字符串转换为文件句柄取决于实现。该字符串的内容及其映射到的内容取决于操作系统。请记住,C++ 可用于编写该操作系统,因此它被用于询问如何遍历目录尚未定义的级别(因为您正在编写目录管理代码)。
Look at your OS API documentation for how to do this. If you need to be portable, you will have to have a bunch of #ifdefs for various OSes.
查看您的 OS API 文档以了解如何执行此操作。如果您需要便携,则必须为各种操作系统提供一堆#ifdef。