递归函数用于列出子目录中的所有文件

10

我正在尝试编写一个函数,该函数返回当前文件夹及其所有子文件夹中所有文件的列表。 我编写了以下代码:

#include <iostream>
#include <dirent.h>
#include <cstring>

using namespace std;

int main() {
   DIR* dir; dirent* pdir;
   //From my workspace
   dir=opendir(".");     
   while (pdir=readdir(dir)) {
       if(/**********This pdir is a directory**********/) {
           /**********RECURSIVE CALL SHOULD BE HERE**********/
           cout<<pdir->d_name<<endl;
       }
   }
   closedir(dir);
   return 0;
}

我在谷歌上搜索了它,但不知道如何:

  • 检查当前的pdir是否是目录
  • 进入目录并对其执行递归调用

同时我将所有东西都放在主函数中,因为我仍然不知道递归函数应该有哪些参数。

有什么提示吗?


针对哪个平台?编辑:按什么顺序?先序、中序还是后序? - Wug
1
请查看http://stackoverflow.com/questions/3844546/how-do-i-check-if-a-directory-is-a-file-or-folder以获取您的第一个问题,然后查看http://msdn.microsoft.com/en-us/library/windows/desktop/aa363806(v=vs.85).aspx或posix调用“chdir(2)”以获取您的第二个问题。 - HRÓÐÓLFR
只要我能列出所有文件,我不关心顺序。 - Maroun
请注意,不能递归调用 main() - Robᵩ
@Robᵩ 确实是这样。我编辑了问题。 - Maroun
2
看一下这个boost filesystem教程。当然你需要这个库。 - didierc
11个回答

14

以下是使用提议中的标准文件系统库的版本:

#include <iostream>
#include <filesystem>

using namespace std;
using namespace std::tr2::sys;

void main()
{   
  for (recursive_directory_iterator i("."), end; i != end; ++i) 
    if (!is_directory(i->path()))
      cout << i->path().filename() << "\n";
} 

2
请注意,std::filesystem现在至少在C++17标准中被接受,因此将使用的命名空间从std::tr2::sys更改为std::filesystem可能就足够了。 - StormByte

9

我在C++11中的方法:

#include <string>
#include <functional>
#include <dirent.h>

void listFiles(const std::string &path, std::function<void(const std::string &)> cb) {
    if (auto dir = opendir(path.c_str())) {
        while (auto f = readdir(dir)) {
            if (!f->d_name || f->d_name[0] == '.') continue;
            if (f->d_type == DT_DIR) 
                listFiles(path + f->d_name + "/", cb);

            if (f->d_type == DT_REG)
                cb(path + f->d_name);
        }
        closedir(dir);
    }
}

使用方法:

listFiles("my_directory/", [](const std::string &path) {
    std::cout << path << std::endl;
});

4
listFiles(path + f->d_name + "/", cb); 这行代码中有一个错误:应该改为 listFiles(path + "/" + f->d_name + "/", cb); - markzzz
这是一个适用于不实现std::filesystem的系统的好答案。 - Sergei

8
除非您的目标是学习如何编写递归函数,否则您可能更喜欢基于Boost.Filesystem的简单循环。请参考以下代码示例:Boost.Filesystem
#include "boost/filesystem.hpp"
#include <iostream>

int main () {
  for ( boost::filesystem::recursive_directory_iterator end, dir("./");
    dir != end; ++dir ) {
    // std::cout << *dir << "\n";  // full path
    std::cout << dir->path().filename() << "\n"; // just last bit
  }
}

甚至可以只使用一个函数调用:
std::copy(
  boost::filesystem::recursive_directory_iterator("./"),
  boost::filesystem::recursive_directory_iterator(),
  std::ostream_iterator<boost::filesystem::directory_entry>(std::cout, "\n"));

你的示例有一个小问题,它列出了目录和文件 - 请参考我的答案来排除目录。 - Paul Jurczak

8

使用C++17的recursive_directory_iterator,代码可以变得更加简洁:

void ls_recursive(const std::filesystem::path& path) {
    for(const auto& p: std::filesystem::recursive_directory_iterator(path)) {
        if (!std::filesystem::is_directory(p)) {
            std::cout << p.path() << '\n';
        }
    }
}

带有示例输出:

"/home/user/prj/rust/stack/Cargo.toml"
"/home/user/prj/rust/stack/.gitignore"
"/home/user/prj/rust/stack/src/main.rs"
"/home/user/prj/rust/stack/.git/config"

5

将该代码隔离在一个过程中,该过程接受基本目录路径作为参数,以便您可以实际执行递归调用。应该是这样的:

void recursive_file_list(const char * directory)
{
    // ...
}

然后,要检查你获取的pdir是否为目录,有两种方法:

  • 您可以检查pdir->d_type==DT_DIR; 这会立即给您这个信息,但它不是可移植的(POSIX没有规定d_type成员的存在);此外,它不支持所有文件系统,因此您可能会得到DT_UNKNOWN。如果您想要跟随符号链接,则还必须执行额外的检查,即使您获得了DT_LNK。在这些情况下,您必须回退到lstat(请参见下面的点);
  • 相反,您可以使用lstat来获取有关每个文件的信息,特别是检查struct statst_mode字段。

即使 d_type 存在,您仍然需要回退到 stat,因为文件系统不一定会填充 d_type,即使操作系统支持它。此外,d_type 值不是位掩码,您只需要 (pdir->d_type == DT_DIR) - zwol

2

它使用标准的C++功能,代码中不需要包含任何第三方库。

只需将目录路径作为参数发送。它将还原该文件夹及其子文件夹中存在的所有文件路径。

此外,如果您需要对特定类型的文件进行排序(即.txt或.jpg),请传递扩展名,它将打印具有相应扩展名的所有文件路径。

#include <Windows.h>
#include<iostream>
#include<vector>
#include<string>
using namespace std;

vector<string> files;

std::string Recursive(std::string folder) {
    std::string search_path = folder + "/*.*";
    WIN32_FIND_DATA fd;
    HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
    std::string tmp;
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                if (!(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))) {
                    tmp = folder + "\\";
                    tmp = tmp + fd.cFileName;
                    Recursive(tmp);
                }
            }
            else {
                std::string FinalFilePath = folder + "\\" + fd.cFileName;
                files.push_back(FinalFilePath);
            }

        } while (::FindNextFile(hFind, &fd));
        ::FindClose(hFind);
    }
    return folder;
}

bool has_suffix(const std::string& str, const std::string& suffix) {
    return str.size() >= suffix.size() &&
        str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

int main(){
std::string folder = "C:\\Users\\Omkar\\Desktop\\Test";
    Recursive(folder);
    std::string t;
    const auto needle = std::string(".txt");
    while (!files.empty()) {
        t = files.back();
        if (has_suffix(t, ".mmt")) {
            cout << "FINAL PATH : " << t << endl;
            t.clear();
        }
        files.pop_back();
    }
return 0;
}

Windows.h不是标准的C++。 - Sergei

1
路径应该看起来像 /your_path/。如果要在隐藏文件夹中进行搜索,您应该添加第三个参数true
#include <dirent.h>
#include <vector>
#include <cstring>    

void GetReqDirs(const std::string& path, std::vector<string>& files,const bool showHiddenDirs = false){
    DIR *dpdf;
    struct dirent *epdf;
    dpdf = opendir(path.c_str());
    if (dpdf != NULL){
        while ((epdf = readdir(dpdf)) != NULL){
            if(showHiddenDirs ? (epdf->d_type==DT_DIR && string(epdf->d_name) != ".." && string(epdf->d_name) != "." ) : (epdf->d_type==DT_DIR && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ) ){
                GetReqDirs(path+epdf->d_name+"/",files, showHiddenDirs);
            }
            if(epdf->d_type==DT_REG){
                files.push_back(path+epdf->d_name);
            }
        }
    }
    closedir(dpdf);
}

0

太棒了!这是我的Windows友好解决方案,仅使用std,因为我在Windows上的上述解决方案中遇到了一些问题,也适用于DLL:

 #include <windows.h> // messagebox

 #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING

 #include <filesystem> // C++17 standard header file name
 #include <experimental/filesystem> // Header file for pre-standard implementation

 using namespace std::experimental::filesystem::v1;

 for (recursive_directory_iterator next(path(dir.c_str())), end; next != end; ++next)
 {
    auto path = next->path();
    if (path.has_extension())
    {
        MessageBox(0, path.wstring().c_str(), L"Filepath", 0);
    }
 }

0
你可以检查字符串中是否没有“.”。
if(strstr(pdir->d_name,".") != NULL)

1
“.” 是一个完全合适的目录名称部分。 - Deduplicator

0
这是我如何使用它
std::vector<std::string> get_all_files_recursive(const fs::path& path)
    {
        std::vector<std::string> result;

        for (const auto& p : fs::recursive_directory_iterator(path))
        {
            if (!fs::is_directory(p))
            {
                fs::path path = p.path();
                result.push_back(path.u8string());
            }
        }

        return result;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接