如何在标准C ++中递归遍历每个文件/目录?
答案 0 :(得分:93)
在标准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;
}
答案 1 :(得分:41)
如果使用Win32 API,您可以使用 FindFirstFile 和 FindNextFile 函数。
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;
}
答案 2 :(得分:31)
使用基于for
和C++11的新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;
}
答案 3 :(得分:27)
在带有“Filesystem 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>
标题中找到(不再是“实验性”)。
答案 4 :(得分: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;
}
答案 5 :(得分:10)
除了上面提到的boost :: filesystem之外,您可能还需要检查wxWidgets::wxDir和Qt::QDir。
wxWidgets和Qt都是开源的跨平台C ++框架。
wxDir
提供了一种使用Traverse()
或更简单的GetAllFiles()
函数递归遍历文件的灵活方法。您也可以使用GetFirst()
和GetNext()
函数实现遍历(我假设Traverse()和GetAllFiles()是最终使用GetFirst()和GetNext()函数的包装器。
QDir
提供对目录结构及其内容的访问。有几种方法可以使用QDir遍历目录。您可以使用QDirIterator :: Subdirectories标志实例化的QDirIterator迭代目录内容(包括子目录)。另一种方法是使用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;
}
答案 6 :(得分: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;
}
答案 7 :(得分:4)
您可以使用ftw(3)
or nftw(3)
在POSIX系统上使用C或C ++遍历文件系统层次结构。
答案 8 :(得分:3)
你没有。 C ++标准没有目录的概念。将字符串转换为文件句柄取决于实现。该字符串的内容及其映射的内容取决于操作系统。请记住,C ++可以用来编写该操作系统,因此它可以用于询问如何迭代目录尚未定义的级别(因为您正在编写目录管理代码)。
查看您的OS API文档,了解如何执行此操作。如果您需要可移植,则必须为各种操作系统提供一堆#ifdef。
答案 9 :(得分:3)
你可能最适合使用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.
答案 10 :(得分:2)
您需要为文件系统遍历调用特定于操作系统的函数,例如open()
和readdir()
。 C标准没有指定任何与文件系统相关的函数。
答案 11 :(得分:2)
您可以使用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;
}
答案 12 :(得分:1)
你没有。标准C ++不公开目录的概念。具体来说,它没有任何方法列出目录中的所有文件。
答案 13 :(得分:0)
我们到了2019年。C++
中有filesystem个标准库。 Filesystem library
提供了用于在文件系统及其组件(例如路径,常规文件和目录)上执行操作的工具。
如果您正在考虑可移植性问题,请注意this link上的重要说明。它说:
如果实现无法访问分层文件系统,或者文件系统库工具未提供必要的功能,则文件系统库工具可能不可用。如果基础文件系统不支持某些功能(例如FAT文件系统缺少符号链接并禁止使用多个硬链接),则某些功能可能不可用。在这种情况下,必须报告错误。
文件系统库最初是作为boost.filesystem
开发的,已发布为技术规范ISO / IEC TS 18822:2015,并最终合并为C ++ 17起的ISO C ++。与C ++ 17库相比,目前在更多编译器和平台上都可以使用boost实现。
@ 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()
成员函数返回{{ 3}},它可以用来获取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;
}
答案 14 :(得分:0)
文件树遍历ftw
是一种将路径遍历整个目录树的递归方法。更多详细信息,请参见here。
注意:您还可以使用fts
跳过诸如.
或..
或.bashrc
的隐藏文件
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
printf("0%3o\t%s\n", status->st_mode&0777, name);
}
if (type == FTW_D && strcmp(".", name) != 0)
{
printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
输出如下:
0755 ./Shivaji/
0644 ./Shivaji/20200516_204454.png
0644 ./Shivaji/20200527_160408.png
0644 ./Shivaji/20200527_160352.png
0644 ./Shivaji/20200520_174754.png
0644 ./Shivaji/20200520_180103.png
0755 ./Saif/
0644 ./Saif/Snapchat-1751229005.jpg
0644 ./Saif/Snapchat-1356123194.jpg
0644 ./Saif/Snapchat-613911286.jpg
0644 ./Saif/Snapchat-107742096.jpg
0755 ./Milind/
0644 ./Milind/IMG_1828.JPG
0644 ./Milind/IMG_1839.JPG
0644 ./Milind/IMG_1825.JPG
0644 ./Milind/IMG_1831.JPG
0644 ./Milind/IMG_1840.JPG
让我们说,如果您想匹配文件名(例如:搜索所有*.jpg, *.jpeg, *.png
文件。)以满足特定需求,请使用fnmatch
。
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <fnmatch.h>
static const char *filters[] = {
"*.jpg", "*.jpeg", "*.png"
};
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
int i;
for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
/* if the filename matches the filter, */
if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
printf("0%3o\t%s\n", status->st_mode&0777, name);
break;
}
}
}
if (type == FTW_D && strcmp(".", name) != 0)
{
//printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
答案 15 :(得分:0)
在C ++ 17上,您可以这样:
#include <filesystem>
#include <iostream>
#include <vector>
namespace fs = std::filesystem;
int main()
{
std::ios_base::sync_with_stdio(false);
for (const auto &entry : fs::recursive_directory_iterator(".")) {
if (entry.path().extension() == ".png") {
std::cout << entry.path().string() << std::endl;
}
}
return 0;
}
答案 16 :(得分:0)
在Windows和Linux上使用C ++ 11(使用experimental/filesystem
)以递归方式获取所有文件名的答案:
对于Windows:
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
void getFiles_w(string path, vector<string>& files) {
intptr_t hFile = 0;
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
do {
if ((fileinfo.attrib & _A_SUBDIR)) {
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("/").append(fileinfo.name), files);
}
else {
files.push_back(p.assign(path).append("/").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
}
}
对于Linux:
#include <experimental/filesystem>
bool getFiles(std::experimental::filesystem::path path, vector<string>& filenames) {
namespace stdfs = std::experimental::filesystem;
// http://en.cppreference.com/w/cpp/experimental/fs/directory_iterator
const stdfs::directory_iterator end{} ;
for (stdfs::directory_iterator iter{path}; iter != end ; ++iter) {
// http://en.cppreference.com/w/cpp/experimental/fs/is_regular_file
if (!stdfs::is_regular_file(*iter)) { // comment out if all names (names of directories tc.) are required
if (getFiles(iter->path(), filenames))
return true;
}
else {
filenames.push_back(iter->path().string()) ;
cout << iter->path().string() << endl;
}
}
return false;
}
在Linux中使用-lstdc++fs
进行编译时,请记住要链接g++
。
答案 17 :(得分:0)
员工 Visual C++ 和 WIN API:
bool Parser::queryDIR(string dir_name) {
vector<string> sameLayerFiles;
bool ret = false;
string dir = "";
//employee wide char
dir = dir_name + "\\*.*";;
//employee WIN File API
WIN32_FIND_DATA fd;
WIN32_FIND_DATA fd_dir;
HANDLE hFind = ::FindFirstFile(getWC(dir.c_str()), &fd);
HANDLE hFind_dir = ::FindFirstFile(getWC(dir.c_str()), &fd_dir);
string str_subdir;
string str_tmp;
//recursive call for diving into sub-directories
do {
if ((fd_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
//ignore trival file node
while(true) {
FindNextFile(hFind_dir, &fd_dir);
str_tmp = wc2str(fd_dir.cFileName);
if (str_tmp.compare(".") && str_tmp.compare("..")){
break;
}
}
if ((fd_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
str_subdir = wc2str(fd_dir.cFileName);
ret = queryDIR(dir_name + "\\" + str_subdir);
}
}
} while(::FindNextFile(hFind_dir, &fd_dir));
//iterate same layer files
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
str_tmp = wc2str(fd.cFileName);
string fname = dir_name + "\\" + str_tmp;
sameLayerFiles.push_back(fname);
}
} while(::FindNextFile(hFind, &fd));
for (std::vector<string>::iterator it=sameLayerFiles.begin(); it!=sameLayerFiles.end(); it++) {
std::cout << "iterated file:" << *it << "..." << std::endl;
//Doing something with every file here
}
return true;
}
希望我的代码可以帮助:)
您可以在 My GitHub
上查看更多详细信息和程序屏幕截图答案 18 :(得分:-1)
如果您使用的是Windows,则可以将FindFirstFile与FindNextFile API一起使用。您可以使用FindFileData.dwFileAttributes来检查给定路径是文件还是目录。如果它是一个目录,您可以递归重复该算法。
在这里,我汇总了一些列出Windows机器上所有文件的代码。