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

How can I get the list of files in a directory using C or C++?

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
", ent->d_name);
  }
  closedir (dir);
} else {
  /* could not open directory */
  perror ("
");
  return EXIT_FAILURE;
}

它只是一个小的头文件,在不使用像boost这样的基于模板的大方法的情况下,它可以完成大多数简单的工作(没有冒犯,我喜欢boost!).

Windows兼容层的作者是Toni Ronkko。在Unix中,它是一个标准头文件。

更新2017:

在C++ 17中,现在有一种正式的方法来列出文件系统的文件:EDCOX1(0)。下面的Shrevardhan给出了一个很好的答案,源代码是:

1
2
3
4
5
6
7
8
9
10
11
#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;
}


C++ 17现在有一个EDCOX1,0,它可以用作

1
2
3
4
5
6
7
8
9
10
11
#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;
}

另外,std::filesystem::recursive_directory_iterator也可以迭代子目录。


不幸的是,C++标准并没有定义以这种方式处理文件和文件夹的标准方式。

由于没有跨平台的方法,所以最好的跨平台方法是使用一个库,比如boost文件系统模块。

跨平台助推法:

The following function, given a directory path and a file name, recursively searches the directory and its sub-directories for the file name, returning a bool, and if successful, the path to the file that was found.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool find_file(const path &amp; dir_path,         // in this directory,
               const std::string &amp; file_name, // search for this name,
               path &amp; 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;
}

来源于上面提到的Boost页面。

对于基于Unix/Linux的系统:

您可以使用opendir/readdir/closedir。

Sample code which searches a directory for entry ``name'' is:

1
2
3
4
5
6
7
8
9
len = strlen(name);
dirp = opendir(".");
while ((dp = readdir(dirp)) != NULL)
        if (dp->d_namlen == len &amp;&amp; !strcmp(dp->d_name, name)) {
                (void)closedir(dirp);
                return FOUND;
        }
(void)closedir(dirp);
return NOT_FOUND;

以上手册页的源代码。

对于基于Windows的系统:

您可以使用win32 api findfirstfile/findnextfile/findclose函数。

The following C++ example shows you a minimal use of FindFirstFile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#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]
"
), argv[0]);
      return;
   }

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

以上msdn页的源代码。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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;
}

PS:正如@sebastian所提到的,您可以将*.*更改为*.ext,以便只获取该目录中的ext文件(即特定类型的文件)。


对于纯C的解决方案,请检查这个。它只需要一个额外的头:

https://github.com/cxong/tinydir

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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("
"
);

    tinydir_next(&dir);
}

tinydir_close(&dir);

与其他选项相比的一些优势:

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


我建议在这个可重用的包装器中使用glob。它生成一个对应于符合glob模式的文件路径的vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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;
}

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

1
vector<string> files = globVector("./*");


下面是C++11中的一个非常简单的代码,它使用boost::filesystem库获取目录中的文件名(不包括文件夹名):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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;
    }
}

输出就像:

1
2
file1.txt
file2.dat


为什么不使用glob()

1
2
3
4
5
6
7
#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;
}


我想,下面的代码片段可以用来列出所有的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
"
,entry->d_name);
    }

    closedir(dir);
}

下面是结构目录的结构

1
2
3
4
5
6
7
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 */
};


尝试X平台方法的Boost

http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm(文件系统)

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
    }

};

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变体的情况。

希望有帮助…


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
char **getKeys(char *data_dir, char* tablename, int *num_keys)
{
    char** arr = malloc(MAX_RECORDS_PER_TABLE*sizeof(char*));
int i = 0;
for (;i < MAX_RECORDS_PER_TABLE; i++)
    arr[i] = malloc( (MAX_KEY_LEN+1) * sizeof(char) );  


char *buf = (char *)malloc( (MAX_KEY_LEN+1)*sizeof(char) );
snprintf(buf, MAX_KEY_LEN+1,"%s/%s", data_dir, tablename);

DIR* tableDir = opendir(buf);
struct dirent* getInfo;

readdir(tableDir); // ignore '.'
readdir(tableDir); // ignore '..'

i = 0;
while(1)
{


    getInfo = readdir(tableDir);
    if (getInfo == 0)
        break;
    strcpy(arr[i++], getInfo->d_name);
}
*(num_keys) = i;
return arr;
}

希望这段代码对您有所帮助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

string wchar_t2string(const wchar_t *wchar)
{
    string str ="";
    int index = 0;
    while(wchar[index] != 0)
    {
        str += (char)wchar[index];
        ++index;
    }
    return str;
}

wchar_t *string2wchar_t(const string &str)
{
    wchar_t wchar[260];
    int index = 0;
    while(index < str.size())
    {
        wchar[index] = (wchar_t)str[index];
        ++index;
    }
    wchar[index] = 0;
    return wchar;
}

vector<string> listFilesInDirectory(string directoryName)
{
    WIN32_FIND_DATA FindFileData;
    wchar_t * FileName = string2wchar_t(directoryName);
    HANDLE hFind = FindFirstFile(FileName, &FindFileData);

    vector<string> listFileNames;
    listFileNames.push_back(wchar_t2string(FindFileData.cFileName));

    while (FindNextFile(hFind, &FindFileData))
        listFileNames.push_back(wchar_t2string(FindFileData.cFileName));

    return listFileNames;
}

void main()
{
    vector<string> listFiles;
    listFiles = listFilesInDirectory("C:\\*.txt");
    for each (string str in listFiles)
        cout << str << endl;
}


精明的回答很管用。但是如果你想在C++ 14中使用它,只需改变EDOCX1,2。

即。,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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;
}

这对我有用。对不起,如果我记不清来源。它可能来自一个手册页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <ftw.h>

int AnalizeDirectoryElement (const char *fpath,
                            const struct stat *sb,
                            int tflag,
                            struct FTW *ftwbuf) {

  if (tflag == FTW_F) {
    std::string strFileName(fpath);

    DoSomethingWith(strFileName);
  }
  return 0;
}

void WalkDirectoryTree (const char * pchFileName) {

  int nFlags = 0;

  if (nftw(pchFileName, AnalizeDirectoryElement, 20, nFlags) == -1) {
    perror("nftw");
  }
}

int main() {
  WalkDirectoryTree("some_dir/");
}

这个实现实现实现了您的目的,用指定目录的内容动态地填充字符串数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int exploreDirectory(const char *dirpath, char ***list, int *numItems) {
    struct dirent **direntList;
    int i;
    errno = 0;

    if ((*numItems = scandir(dirpath, &direntList, NULL, alphasort)) == -1)
        return errno;

    if (!((*list) = malloc(sizeof(char *) * (*numItems)))) {
        fprintf(stderr,"Error in list allocation for file list: dirpath=%s.
"
, dirpath);
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < *numItems; i++) {
        (*list)[i] = stringDuplication(direntList[i]->d_name);
    }

    for (i = 0; i < *numItems; i++) {
        free(direntList[i]);
    }

    free(direntList);

    return 0;
}


您可以使用std::experimental::filesystem::directory_迭代器()直接获取根目录中的所有文件。然后,读取这些路径文件的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <filesystem>
#include <string>
#include <direct.h>
using namespace std;
namespace fs = std::experimental::filesystem;
void ShowListFile(string path)
{
for(auto &p: fs::directory_iterator(path))  /*get directory */
     cout<<p.path().filename()<<endl;   // get file name
}

int main() {

ShowListFile("C:/Users/dell/Pictures/Camera Roll/");
getchar();
return 0;
}

系统调用它!

1
system("dir /b /s /a-d * > file_names.txt" );

那就读一下文件。

编辑:这个答案应该被认为是一个黑客,但如果你没有更优雅的解决方案,它确实可以工作(尽管是以平台特定的方式)。


此答案适用于在使用Visual Studio和其他任何答案时遇到问题的Windows用户。

  • 从github页面下载dirent.h文件。但最好只使用原始dirent.h文件并按照下面的步骤(这是我如何使其工作的)。

    用于dirent.h的github页面用于windows:github用于dirent.h的页面

    原始目录文件:原始目录.h文件

  • 转到您的项目并添加一个新项(ctrl+shift+a)。添加头文件(.h)并将其命名为dirent.h。

  • 将原始dirent.h文件代码粘贴到头中。

  • 在代码中包含"dirent.h"。

  • 在代码中放入下面的void filefinder()方法,并从main函数调用它,或者编辑您想要如何使用它的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <stdio.h>
    #include <string.h>
    #include"dirent.h"

    string path ="C:/folder"; //Put a valid path here for folder

    void filefinder()
    {
        DIR *directory = opendir(path.c_str());
        struct dirent *direntStruct;

        if (directory != NULL) {
            while (direntStruct = readdir(directory)) {
                printf("File Name: %s
    "
    , direntStruct->d_name); //If you are using <stdio.h>
                //std::cout << direntStruct->d_name << std::endl; //If you are using <iostream>
            }
        }
        closedir(directory);
    }

  • 只是一些我想分享和感谢你的阅读材料。玩弄一下这个函数来理解它。你可能喜欢。E代表延伸,P代表路径,S代表路径分隔符。

    如果传递路径时没有结束分隔符,则会在路径后面附加一个分隔符。对于扩展名,如果输入空字符串,则函数将返回其名称中没有扩展名的任何文件。如果输入了一个星,那么目录中的所有文件都将被返回。如果e长度大于0但不是一个*那么,如果e在零位置没有包含一个点,则一个点将被预填到e。

    返回值。如果返回零长度映射,则未找到任何内容,但目录打开正常。如果返回值中的索引999可用,但映射大小仅为1,则表示打开目录路径时出现问题。

    注意,为了提高效率,这个函数可以分为3个较小的函数。除此之外,您还可以创建一个调用者函数,它将根据输入检测要调用的函数。为什么效率更高?说,如果你要抓取所有的文件,这样做的方法,为抓取所有的文件而构建的子功能将只抓取所有的文件,不需要评估任何其他不必要的条件,每次它找到一个文件。

    这也适用于您抓取没有扩展名的文件。为此目的而构建的特定函数仅在找到的对象是文件时评估天气,然后评估文件名中是否有点。

    如果您只读取没有那么多文件的目录,那么保存可能不多。但是,如果您正在读取大量目录,或者目录中有几十万个文件,那么这可能是一个巨大的节省。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    #include <stdio.h>
    #include <sys/stat.h>
    #include <iostream>
    #include <dirent.h>
    #include <map>

    std::map<int, std::string> getFile(std::string p, std::string e ="", unsigned char s = '/'){
        if ( p.size() > 0 ){
            if (p.back() != s) p += s;
        }
        if ( e.size() > 0 ){
            if ( e.at(0) != '.' && !(e.size() == 1 && e.at(0) == '*') ) e ="." + e;
        }

        DIR *dir;
        struct dirent *ent;
        struct stat sb;
        std::map<int, std::string> r = {{999,"FAILED"}};
        std::string temp;
        int f = 0;
        bool fd;

        if ( (dir = opendir(p.c_str())) != NULL ){
            r.erase (999);
            while ((ent = readdir (dir)) != NULL){
                temp = ent->d_name;
                fd = temp.find(".") != std::string::npos? true : false;
                temp = p + temp;

                if (stat(temp.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)){
                    if ( e.size() == 1 && e.at(0) == '*' ){
                        r[f] = temp;
                        f++;
                    } else {
                        if (e.size() == 0){
                            if ( fd == false ){
                                r[f] = temp;
                                f++;
                            }
                            continue;
                        }

                        if (e.size() > temp.size()) continue;

                        if ( temp.substr(temp.size() - e.size()) == e ){
                            r[f] = temp;
                            f++;
                        }
                    }
                }
            }

            closedir(dir);
            return r;
        } else {
            return r;
        }
    }

    void printMap(auto &m){
        for (const auto &p : m) {
            std::cout <<"m[" << p.first <<"] =" << p.second << std::endl;
        }
    }

    int main(){
        std::map<int, std::string> k = getFile("./","");
        printMap(k);
        return 0;
    }

    由于目录的文件和子目录通常存储在树结构中,一种直观的方法是使用DFS算法递归地遍历每个目录。下面是在Windows操作系统中使用IO.H中的基本文件函数的一个例子。您可以在其他平台中替换这些函数。我想表达的是,DFS的基本思想完全符合这个问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include<io.h>
    #include<iostream.h>
    #include<string>
    using namespace std;

    void TraverseFilesUsingDFS(const string& folder_path){
       _finddata_t file_info;
       string any_file_pattern = folder_path +"\\*";
       intptr_t handle = _findfirst(any_file_pattern.c_str(),&file_info);
       //If folder_path exsist, using any_file_pattern will find at least two files"." and"..",
       //of which"." means current dir and".." means parent dir
       if (handle == -1){
           cerr <<"folder path not exist:" << folder_path << endl;
           exit(-1);
       }
       //iteratively check each file or sub_directory in current folder
       do{
           string file_name=file_info.name; //from char array to string
           //check whtether it is a sub direcotry or a file
           if (file_info.attrib & _A_SUBDIR){
                if (file_name !="." && file_name !=".."){
                   string sub_folder_path = folder_path +"\" + file_name;                
                   TraverseFilesUsingDFS(sub_folder_path);
                   cout <<"
    a sub_folder path:" << sub_folder_path << endl;
                }
           }
           else
                cout <<"
    file name:" << file_name << endl;
        } while (_findnext(handle, &file_info) == 0);
        //
        _findclose(handle);
    }

    我试着遵循两个答案中给出的例子,可能值得注意的是,似乎std::filesystem::directory_entry已被更改为不具有<<运算符的过载。我必须使用以下代码来编译和运行它,而不是使用std::cout << p << std::endl;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    #include <filesystem>
    #include <string>
    namespace fs = std::filesystem;

    int main() {
        std::string path ="/path/to/directory";
        for(const auto& p : fs::directory_iterator(path))
            std::cout << p.path() << std::endl;
    }

    试图将p单独传递给std::cout <<,导致缺少过载错误。


    这对我有用。它只使用所有文件的名称(没有路径)写入文件。然后它读取该txt文件并为您打印。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void DisplayFolderContent()
        {

            system("dir /n /b * > file_names.txt");
            char ch;
            std::fstream myStream("file_names.txt", std::fstream::in);
            while (myStream.get(ch))
            {
                std::cout << ch;
            }

        }