首页 文章

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

提问于
浏览
471

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

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

24 回答

  • 217

    为什么不使用 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;
    }
    
  • 10

    这是一个非常简单的代码,在 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;
        }
    }
    

    输出如下:

    file1.txt
    file2.dat
    
  • 3

    在小而简单的任务中我不使用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;
    }
    

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

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

    UPDATE 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;
    }
    

    如果你正在使用C 17方法,请考虑提出他的答案 .

  • 2

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

    由于没有跨平台的方式,最好的跨平台方式是使用诸如boost filesystem module之类的库 .

    Cross platform boost method:

    给定目录路径和文件名的以下函数以递归方式在目录及其子目录中搜索文件名,返回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;
    }
    

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


    For Unix/Linux based systems:

    您可以使用opendir / readdir / closedir .

    在目录中搜索条目“名称”的示例代码是:

    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;
    

    上述手册页的源代码 .


    For a windows based systems:

    您可以使用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);
       }
    }
    

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

  • 4

    看看这个使用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;
        }
    
    };
    
  • 26

    这对我有用 . 如果我不记得来源,我很抱歉 . 它可能来自手册页 .

    #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/");
    }
    
  • 8

    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
    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;
    }
    
  • 46

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

    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;
            }
    
        }
    
  • 75

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

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

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

  • 6

    我希望这段代码可以帮到你 .

    #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;
    }
    
  • 17

    此答案应适用于使用Visual Studio以及任何其他答案进行此操作时遇到问题的Windows用户 .

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

    针对Windows的dirent.h的Github页面:Github page for dirent.h

    原始指令文件:Raw dirent.h File

    • 转到项目并添加新项目(Ctrl Shift A) . 添加头文件(.h)并将其命名为dirent.h .

    • Raw dirent.h File代码粘贴到 Headers 中 .

    • 在您的代码中包含“dirent.h” .

    • 将以下 void filefinder() 方法放在代码中并从 main 函数中调用它或编辑函数的使用方法 .

    #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\n", direntStruct->d_name); //If you are using <stdio.h>
                //std::cout << direntStruct->d_name << std::endl; //If you are using <iostream>
            }
        }
        closedir(directory);
    }
    
  • 2

    Shreevardhan的回答非常好 . 但如果你想在c 14中使用它,只需进行更改 namespace fs = experimental::filesystem;

    即,

    #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;
    }
    
  • 21

    您可以使用std :: experimental :: filesystem :: directory_iterator()获取根目录中的所有文件 . 然后,读取这些路径文件的名称 .

    #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;
    }
    
  • 0

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

    #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);
    }
    

    以下是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 */
    };
    
  • 0

    由于目录的文件和子目录通常存储在树结构中,因此直观的方法是使用DFS算法递归遍历每个目录 . 以下是使用io.h中的基本文件函数在Windows操作系统中的示例 . 您可以在其他平台中替换这些功能 . 我想表达的是DFS的基本思想完全满足了这个问题 .

    #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);
    }
    
  • 2

    此实现实现了您的目的,使用指定目录的内容动态填充字符串数组 .

    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.\n", 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;
    }
    
  • 16

    系统调用它!

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

    然后只需阅读该文件 .

    编辑:这个答案应该被认为是一个黑客,但它确实有效(如果您无法访问更优雅的解决方案,则以平台特定的方式) .

  • 1

    我试图遵循both answers中给出的示例,可能值得注意的是,似乎 std::filesystem::directory_entry 已被更改为没有 << 运算符的重载 . 而不是 std::cout << p << std::endl; 我必须使用以下代码才能编译并使其工作:

    #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 << 导致缺少重载错误 .

  • 656

    我建议使用 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;
    }
    

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

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

    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;
    }
    

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

  • 0

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

    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);
    

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

    • 它是可移植的 - 包装POSIX dirent和Windows FindFirstFile

    • 它在可用的地方使用 readdir_r ,这意味着它(通常)线程安全

    • 通过相同的 UNICODE 宏支持Windows UTF-16

    • 这是C90所以即使非常古老的编译器也可以使用它

  • -1

    我想分享一些内容并感谢您阅读材料 . 玩这个功能有点了解它 . 你可能会喜欢它 . e表示扩展,p表示路径,s表示路径分隔符 .

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

    对于返回值 . 如果返回零长度映射,则找不到任何内容,但目录打开正常 . 如果索引999可以从返回值获得,但 Map 大小只有1那么这意味着打开目录路径时出现问题 .

    请注意,为了提高效率,此功能可以分为3个较小的功能 . 最重要的是,您可以创建一个调用函数,它将根据输入检测它将调用哪个函数 . 为什么效率更高?如果你要 grab 文件的所有内容,那么执行该方法时,为抓取所有文件而构建的子函数将只抓取所有文件,并且每次找到文件时都不需要评估任何其他不必要的条件 .

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

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

    #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;
    }
    
  • 2

    One function is enough, you don't need to use any 3rd-party library (for 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;
    }
    

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

相关问题