如何使用C或C++获取目录中的文件列表?

sam*_*moz 547 c c++ directory file

如何从C或C++代码中确定目录中的文件列表?

我不允许执行ls命令并从我的程序中解析结果.

Pet*_*ker 758

在小而简单的任务中我不使用boost,我使用dirent.h,它也适用于windows:

DIR *dir;
struct dirent *ent;
if ((dir = opendir ("c:\\src\\")) != NULL) {
  /* print all the files and directories within directory */
  while ((ent = readdir (dir)) != NULL) {
    printf ("%s\n", ent->d_name);
  }
  closedir (dir);
} else {
  /* could not open directory */
  perror ("");
  return EXIT_FAILURE;
}
Run Code Online (Sandbox Code Playgroud)

它只是一个小的头文件,可以完成你需要的大部分简单的东西,而不需要使用像boost这样的基于模板的大方法(没有冒犯,我喜欢boost!).

Windows兼容层的作者是Toni Ronkko.在Unix中,它是标准头.

2017年更新:

在C++ 17中,现在有一种列出文件系统文件的官方方法:std::filesystem.下面的Shreevardhan有一个很好的答案,源代码如下:

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
    std::string path = "/path/to/directory";
    for (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 人们变得真实!这是2009年的一个问题,甚至没有提到VS. 所以不要批评你的全部专有(虽然相当不错)IDE不支持几个世纪的操作系统标准.此外,我的回答说它是"可用"的窗口,从现在和任何时候都没有"包含"在任何IDE ...我很确定你可以下载dirent并把它放在一些包括目录和瞧它在那里. (36认同)
  • @JoshC:因为*ent只是内部表示的返回指针.通过关闭目录,您也将消除*ent.由于*ent仅供阅读,我认为这是一个理智的设计. (7认同)
  • 在C++ 14中有`std :: experimental :: filesystem`,C++ 17有`std :: filesystem`.请参阅下面的Shreevardhan的回答.所以不需要第三方库. (7认同)
  • 答案是误导性的.它应该以:"*...我使用**dirent.h**,其中Windows开源兼容层[也存在](https://github.com/tronkko/dirent)*". (6认同)
  • @ArtOfWarfare:当这个问题得到解答时,甚至还没有创建tinydir.它也是一个包围dirent(POSIX)和FindFirstFile(Windows)的包装器,而dirent.h只是包含了一个直接用于windows.我认为这是个人品味,但dirent.h更像是标准 (5认同)
  • 不要浪费你的时间 - **MSVC中没有`<dirent.h>`,`opendir`等!**我不明白为什么人们会回答这个问题 (3认同)
  • &lt;dirent.h&gt; 在 VS15 上不存在。 (2认同)
  • @rustyx 因为它 (a) 提供了到 Windows 兼容层的链接 (b) 从来没有说过该解决方案在任何地方的所有机器上都是开箱即用的 (c) 有效 (2认同)

Shr*_*han 297

C++ 17现在有一个std::filesystem::directory_iterator,可以用作

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() {
    std::string path = "/path/to/directory";
    for (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

此外,std::filesystem::recursive_directory_iterator还可以迭代子目录.

  • AFAIK也可以在C++ 14中使用,但它仍然是实验性的:`namespace fs = std :: experimental :: filesystem;`.它似乎工作正常. (23认同)
  • 这应该是当前使用的首选答案(从C++ 17开始) (6认同)
  • 当将`std :: filesystem :: path`传递给`std :: cout`时要注意,引号包含在输出中。为了避免这种情况,请将`.string()`附加到路径上以进行显式转换,而不是隐式转换(此处为`std :: cout &lt;&lt; p.string()&lt;&lt; std :: endl;`)。示例:http://coliru.stacked-crooked.com/view?id = a55ea60bbd36a8a3 (3认同)
  • 文件名中的非ASCII字符呢?应该不使用`std :: wstring`还是迭代器的类型是什么? (2认同)
  • @BjörnLarsson 我上面评论中的示例不完整,因为答案中的“p”是 [`directory_iterator`](https://en.cppreference.com/w/cpp/filesystem/directory_iterator) 而不是路径(得到对此感到困惑,因为术语“p”通常用于“std::filesystem::path”,抱歉)。`p.path().string()` 分别用于窄字符串`p.path().wstring()` 用于宽字符串应该可以工作。 (2认同)
  • 我不确定我是否是唯一一个这样做的人,但如果没有链接到“-lstdc++fs”,我会得到一个“SIGSEGV(地址边界错误)”。我在文档中找不到任何需要此操作的地方,并且链接器也没有提供任何线索。这适用于“g++ 8.3.0”和“clang 8.0.0-3”。有谁知道文档/规范中指定的此类内容? (2认同)

Bri*_*ndy 228

不幸的是,C++标准没有以这种方式定义使用文件和文件夹的标准方法.

由于没有跨平台方式,最好的跨平台方式是使用诸如boost文件系统模块之类的库.

跨平台提升方法:

给定目录路径和文件名的以下函数以递归方式在目录及其子目录中搜索文件名,返回bool,如果成功,则返回找到的文件的路径.

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;
}
Run Code Online (Sandbox Code Playgroud)

来自上面提到的提升页面的来源.


对于基于Unix/Linux的系统:

你可以使用opendir/readdir/closedir.

在目录中搜索条目"name"的示例代码是:

len = strlen(name);
dirp = opendir(".");
while ((dp = readdir(dirp)) != NULL)
        if (dp->d_namlen == len && !strcmp(dp->d_name, name)) {
                (void)closedir(dirp);
                return FOUND;
        }
(void)closedir(dirp);
return NOT_FOUND;
Run Code Online (Sandbox Code Playgroud)

上述手册页的源代码.


对于基于Windows的系统:

您可以使用Win32 API FindFirstFile/FindNextFile/FindClose函数.

以下C++示例显示了FindFirstFile的最小使用.

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

void _tmain(int argc, TCHAR *argv[])
{
   WIN32_FIND_DATA FindFileData;
   HANDLE hFind;

   if( argc != 2 )
   {
      _tprintf(TEXT("Usage: %s [target_file]\n"), argv[0]);
      return;
   }

   _tprintf (TEXT("Target file is %s\n"), argv[1]);
   hFind = FindFirstFile(argv[1], &FindFileData);
   if (hFind == INVALID_HANDLE_VALUE) 
   {
      printf ("FindFirstFile failed (%d)\n", GetLastError());
      return;
   } 
   else 
   {
      _tprintf (TEXT("The first file found is %s\n"), 
                FindFileData.cFileName);
      FindClose(hFind);
   }
}
Run Code Online (Sandbox Code Playgroud)

来自上述msdn页面的源代码.

  • 在C++ 14中有`std :: experimental :: filesystem`,C++ 17有`std :: filesystem`,它具有与boost类似的功能(libs来自boost).请参阅下面的Shreevardhan的回答. (7认同)

her*_*tao 84

一个功能就足够了,您不需要使用任何第三方库(适用于Windows).

#include <Windows.h>

vector<string> get_all_files_names_within_folder(string folder)
{
    vector<string> names;
    string search_path = folder + "/*.*";
    WIN32_FIND_DATA fd; 
    HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd); 
    if(hFind != INVALID_HANDLE_VALUE) { 
        do { 
            // read all (real) files in current folder
            // , delete '!' read other 2 default folder . and ..
            if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
                names.push_back(fd.cFileName);
            }
        }while(::FindNextFile(hFind, &fd)); 
        ::FindClose(hFind); 
    } 
    return names;
}
Run Code Online (Sandbox Code Playgroud)

PS:如@Sebastian所述,您可以更改*.**.ext仅获取该目录中的EXT文件(即特定类型).

  • 此解决方案如果特定于平台.这就是您需要第三方库的原因. (20认同)
  • @kraxor是的,它只适用于Windows,但OP从未要求拥有跨平台解决方案.顺便说一句,我总是喜欢在不使用第三库的情况下选择一些东西(如果可能的话). (9认同)
  • @herohuyongtao OP从未指定过一个平台,并且为一般性问题提供一个高度依赖平台的解决方案可能会产生误导.(如果只有PlayStation 3的单行解决方案怎么办?这是一个很好的答案吗?)我看到你编辑了你的答案,声明它只适用于Windows,我想这样就好了. (7认同)
  • 多亏了这个,我在这里实现了它:https://gist.github.com/xandout/8443812 (3认同)
  • @herohuyongtao OP提到他无法解析ls,这意味着他可能在unix上..无论如何,对于Windows来说这是一个很好的答案。 (3认同)
  • 我最终使用了`std :: vector <std :: wstring>`然后``fileName.c_str()`而不是字符串向量,这些字符串无法编译. (2认同)

con*_*gus 50

对于仅限C的解决方案,请查看此信息.它只需要一个额外的标题:

https://github.com/cxong/tinydir

tinydir_dir dir;
tinydir_open(&dir, "/path/to/dir");

while (dir.has_next)
{
    tinydir_file file;
    tinydir_readfile(&dir, &file);

    printf("%s", file.name);
    if (file.is_dir)
    {
        printf("/");
    }
    printf("\n");

    tinydir_next(&dir);
}

tinydir_close(&dir);
Run Code Online (Sandbox Code Playgroud)

与其他选项相比有一些优势

  • 它是可移植的 - 包装POSIX dirent和Windows FindFirstFile
  • 它使用readdir_r可用的地方,这意味着它(通常)线程安全
  • 通过相同的UNICODE宏支持Windows UTF-16
  • 它是C90,所以即使非常古老的编译器也可以使用它

  • 非常好的建议.我还没有在Windows计算机上测试它,但它在OS X上运行得非常出色. (2认同)
  • 在Windows上也可以完美地工作。 (2认同)

Chr*_*ord 29

我建议使用glob这个可重复使用的包装器.它生成一个vector<string>适合glob模式的文件路径:

#include <glob.h>
#include <vector>
using std::vector;

vector<string> globVector(const string& pattern){
    glob_t glob_result;
    glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
    vector<string> files;
    for(unsigned int i=0;i<glob_result.gl_pathc;++i){
        files.push_back(string(glob_result.gl_pathv[i]));
    }
    globfree(&glob_result);
    return files;
}
Run Code Online (Sandbox Code Playgroud)

然后可以使用正常的系统通配符模式调用,例如:

vector<string> files = globVector("./*");
Run Code Online (Sandbox Code Playgroud)

  • 测试glob()返回零. (2认同)

Bad*_*Bad 23

这是一个非常简单的代码,C++11使用boost::filesystem库来获取目录中的文件名(文件夹名称除外):

#include <string>
#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;

int main()
{
    path p("D:/AnyFolder");
    for (auto i = directory_iterator(p); i != directory_iterator(); i++)
    {
        if (!is_directory(i->path())) //we eliminate directories
        {
            cout << i->path().filename().string() << endl;
        }
        else
            continue;
    }
}
Run Code Online (Sandbox Code Playgroud)

输出如下:

file1.txt
file2.dat
Run Code Online (Sandbox Code Playgroud)

  • @Alexander De Leon:您可以在他们的网站http://www.boost.org/上获取此库,首先阅读入门指南,然后使用他们的`boost :: filesystem`库http://www.boost.org/ DOC /库/ 1_58_0 /库/文件系统/ DOC/index.htm的 (2认同)

Mee*_*ohi 22

为什么不用glob()

#include <glob.h>

glob_t glob_result;
glob("/your_directory/*",GLOB_TILDE,NULL,&glob_result);
for(unsigned int i=0; i<glob_result.gl_pathc; ++i){
  cout << glob_result.gl_pathv[i] << endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 适用于C编程(在Mac OS X上测试过)! (2认同)
  • 测试glob()返回零! (2认同)

Shr*_*ant 19

我想,下面的代码片段可用于列出所有文件.

#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>

static void list_dir(const char *path)
{
    struct dirent *entry;
    DIR *dir = opendir(path);
    if (dir == NULL) {
        return;
    }

    while ((entry = readdir(dir)) != NULL) {
        printf("%s\n",entry->d_name);
    }

    closedir(dir);
}
Run Code Online (Sandbox Code Playgroud)

以下是struct dirent的结构

struct dirent {
    ino_t d_ino; /* inode number */
    off_t d_off; /* offset to the next dirent */
    unsigned short d_reclen; /* length of this record */
    unsigned char d_type; /* type of file */
    char d_name[256]; /* filename */
};
Run Code Online (Sandbox Code Playgroud)


Tim*_*Tim 10

尝试使用x-platform方法进行提升

http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm

或者只是使用您的操作系统特定文件.

  • 虽然此链接可以回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会变得无效。- [来自评论](/review/low-quality-posts/20149263) (3认同)

小智 8

看看这个使用win32 api的类.只需通过提供foldername您想要列表的实例来构造实例,然后调用该getNextFile方法filename从目录中获取下一个实例.我认为它需要windows.hstdio.h.

class FileGetter{
    WIN32_FIND_DATAA found; 
    HANDLE hfind;
    char folderstar[255];       
    int chk;

public:
    FileGetter(char* folder){       
        sprintf(folderstar,"%s\\*.*",folder);
        hfind = FindFirstFileA(folderstar,&found);
        //skip .
        FindNextFileA(hfind,&found);        
    }

    int getNextFile(char* fname){
        //skips .. when called for the first time
        chk=FindNextFileA(hfind,&found);
        if (chk)
            strcpy(fname, found.cFileName);     
        return chk;
    }

};
Run Code Online (Sandbox Code Playgroud)


Hom*_*er6 6

GNU手册FTW

http://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html#Simple-Directory-Lister

此外,有时候直接去源(双关语)是好的.通过查看Linux中一些最常见命令的内部结构,您可以学到很多东西.我在github上设置了一个GNU coreutils的简单镜像(用于阅读).

https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c

也许这不涉及Windows,但是使用这些方法可以使用许多使用Unix变体的情况.

希望有帮助......


小智 6

Shreevardhan 的回答效果很好。但是如果你想在 c++14 中使用它,只需进行更改namespace fs = experimental::filesystem;

IE,

#include <string>
#include <iostream>
#include <filesystem>

using namespace std;
namespace fs = experimental::filesystem;

int main()
{
    string path = "C:\\splits\\";
    for (auto & p : fs::directory_iterator(path))
        cout << p << endl;
    int n;
    cin >> n;
}
Run Code Online (Sandbox Code Playgroud)


小智 6

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() {
    std::string path = "/path/to/directory";
    for (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)