【问题标题】:How can I get the list of files in a directory using C or C++?如何使用 C 或 C++ 获取目录中的文件列表?
【发布时间】:2020-05-04 02:55:41
【问题描述】:

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

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

【问题讨论】:

  • 这是609236的副本
  • @chrish - 是的,但这个有经典的“我不允许执行'ls'”!这正是我在计算机科学第一年的感受。 ;D
  • C 和 C++ 不是同一种语言。因此,两种语言完成此任务的过程将有所不同。请选择一个并相应地重新标记。
  • 而且这些语言(自 C++17 以来的 C++ 除外)甚至都没有目录的概念 - 因此任何答案都可能取决于您的操作系统,或者您可能依赖的任何抽象库正在使用。

标签: c++ c file directory


【解决方案1】:

为 x-platform 方法尝试 boost

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

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

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
  • @ice1000 认真的吗?此问答来自 2009 年
【解决方案2】:

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

既然没有跨平台的方式,最好的跨平台方式就是使用boost filesystem module之类的库。

跨平台提升方式:

下面的函数,给定一个目录路径和一个文件名,递归地在目录及其子目录中搜索文件名,返回一个布尔值,如果成功,返回找到的文件的路径。

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

来自上面提到的提升页面。

对于基于 Unix/Linux 的系统:

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

来自上述手册页的源代码。

对于基于 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);
   }
}

以上msdn页面的源代码。

【讨论】:

  • 用法:FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &amp;findFileData);
  • 对于 C++14,有 std::experimental::filesystem,对于 C++17,有 std::filesystem,它们的功能与 boost 相似(库是从 boost 派生的)。请参阅下面 Shreevardhan 的答案。
【解决方案3】:

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

旧答案:

在不使用 boost 的小而简单的任务中,我使用 dirent.h。它在 UNIX 中可作为标准标头使用,也可通过 compatibility layer created by Toni Ronkko 用于 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!)。

【讨论】:

  • @ArtOfWarfare:回答这个问题时甚至没有创建 tinydir。它也是 dirent (POSIX) 和 FindFirstFile (Windows) 的包装,而 dirent.h 只是为 windows 包装 dirent。我认为这是个人品味,但 dirent.h 感觉更像是一个标准
  • @JoshC:因为 *ent 只是内部表示的返回指针。通过关闭目录,您也将消除 *ent。由于 *ent 仅用于阅读,我认为这是一个理智的设计。
  • 人们变得真实!这是2009年的一个问题,甚至没有提到VS。因此,不要批评您的完全专有(尽管相当不错)IDE 不支持数百年的操作系统标准。此外,我的回答说它对 Windows“可用”,从现在到任何时候都不会“包含”在任何 IDE 中……我很确定您可以下载 dirent 并将其放入一些包含目录中,瞧,它就在那里。
  • 答案具有误导性。它应该以:“...我使用 dirent.h 开头,Windows 开源兼容层 also exists”。
  • 对于 C++14,有 std::experimental::filesystem,对于 C++17,有 std::filesystem。请参阅下面的 Shreevardhan 的答案。所以不需要 3rd 方库。
【解决方案4】:

查看这个使用 win32 api 的类。只需通过提供您想要列表的foldername 来构造一个实例,然后调用getNextFile 方法从目录中获取下一个filename。我认为它需要windows.h 和stdio.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;
    }

};

【讨论】:

  • 你会在哪里关闭句柄?
【解决方案5】:

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

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

【讨论】:

  • -1。 string2wchar_t 返回局部变量的地址。此外,您可能应该使用 WinAPI 中提供的转换方法,而不是编写自己的方法。
【解决方案6】:
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;
}

【讨论】:

    【解决方案7】:

    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 变体。

    希望对您有所帮助...

    【讨论】:

      【解决方案8】:

      对于仅限 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);
      

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

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

      【讨论】:

      • 非常好的建议。我还没有在 Windows 计算机上测试过它,但它在 OS X 上运行良好。
      • 该库不支持 std::string,因此您无法将 file.c_str() 传递给 tinydir_open。在这种情况下,它在 msvc 2015 上编译期间会出现错误 C2664。
      • @StepanYakovenko 作者明确指出“仅适用于 C 解决方案”
      【解决方案9】:

      我觉得,下面的sn-p可以用来列出所有的文件。

      #include <stdio.h>
      #include <dirent.h>
      #include <sys/types.h>
      
      int main(int argc, char** argv) { 
          list_dir("myFolderName");
          return EXIT_SUCCESS;
      }  
      
      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);
      }
      

      这是使用的结构(存在于dirent.h):

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

      【讨论】:

      • 我想要这个。
      • 这在 C++11 中为我完成了这项工作,而无需使用 Boost 等。很好的解决方案!
      • 这很好!我应该按什么顺序获取文件?
      【解决方案10】:

      一个函数就够了,你不需要使用任何 3rd-party 库(适用于 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 文件(即特定类型)。

      【讨论】:

      • 此解决方案(如果特定于平台)。这就是您需要 3rd 方库的原因。
      • @kraxor 是的,它只适用于 Windows,但 OP 从不要求提供跨平台解决方案。顺便说一句,我总是更喜欢在不使用第三个库的情况下选择一些东西(如果可能的话)。
      • @herohuyongtao OP 从未指定平台,并且为一般问题提供高度依赖平台的解决方案可能会产生误导。 (如果有一个仅适用于 PlayStation 3 的单线解决方案怎么办?这是一个好的答案吗?)我看到您编辑了您的答案以声明它仅适用于 Windows,我想这样就可以了。
      • @herohuyongtao OP 提到他无法解析 ls,这意味着他可能在 unix 上。无论如何,Windows 的好答案。
      • 我最终使用了std::vector&lt;std::wstring&gt;,然后是fileName.c_str(),而不是无法编译的字符串向量。
      【解决方案11】:

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

      【讨论】:

      • 如果您解释所需的包含,这可能是一个更好的答案。
      • 测试 glob() 返回零!
      • 当您知道要查找的文件(例如 *.txt)时,这很好
      【解决方案12】:

      系统调用它!

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

      然后只需读取文件。

      编辑:这个答案应该被认为是一个 hack,但如果您无法获得更优雅的解决方案,它确实有效(尽管以特定于平台的方式)。

      【讨论】:

      • 我不允许执行“ls”命令并从我的程序中解析结果。我知道会有人会发送这样的东西......
      • 对于 Windows,这是迄今为止最实用的方式。请特别注意/A 开关。无论您选择哪种方式,安全性都会在这里严重阻碍。如果一个人不是从一开始就“编码”。 Windows 模拟、身份验证和其他“沙漠”绝非易事。
      【解决方案13】:

      我建议将glob 与这个可重复使用的包装器一起使用。它会生成一个vector&lt;string&gt;,对应于符合 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("./*");
      

      【讨论】:

      • 测试 glob() 返回零。
      • 我想按照您的建议使用 glob.h。但是,我仍然不能包含 .h 文件:上面写着 No such file or directory。你能告诉我如何解决这个问题吗?
      • 请注意,此例程仅深入一级(无递归)。它也不会快速检查以确定它是文件还是目录,您可以通过将GLOB_TILDE 与GLOB_TILDE | GLOB_MARK 切换然后检查以斜杠结尾的路径来轻松完成。如果需要,您必须对其进行修改。
      • 这个跨平台兼容吗?
      • 很遗憾,您无法通过glob 找到统一隐藏的文件。
      【解决方案14】:

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

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

      【讨论】:

      • 我怎么称呼这个?当我尝试在第一个 if 块上运行此函数时,我遇到了段错误。我用char **list; int numItems; exploreDirectory("/folder",list, numItems);调用它
      【解决方案15】:

      这是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
      

      【讨论】:

      • 嗨,我在哪里可以得到这个库?
      • @Alexander De Leon:您可以在他们的网站boost.org 上获得这个库,首先阅读入门指南,然后使用他们的boost::filesystem 库boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
      • @Bad 我将如何更改它以输出每个文件的完整目录。像我想要 D:/AnyFolder/file1.txt 等等?
      【解决方案16】:

      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 也可以迭代子目录。

      【讨论】:

      • AFAIK 也可以在 C++14 中使用,但仍处于试验阶段:namespace fs = std::experimental::filesystem;。不过,它似乎工作正常。
      • 这应该是当前使用的首选答案(从C++17开始)
      • 注意将std::filesystem::path 传递给std::cout 时,输出中包含引号。为避免这种情况,请将.string() 附加到路径以进行显式转换而不是隐式转换(此处为std::cout &lt;&lt; p.string() &lt;&lt; std::endl;)。示例:coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
      • 文件名中的非 ASCII 字符怎么样?不应该使用std::wstring 或者迭代器的类型是什么?
      • 我不确定我是否独自一人,但如果没有链接到-lstdc++fs,我会得到一个SIGSEGV (Address boundary error)。我在文档中找不到任何需要这样做的地方,链接器也没有提供任何线索。这对g++ 8.3.0 和clang 8.0.0-3 都有效。有没有人知道文档/规范中指定了这样的内容?
      【解决方案17】:

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

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

      【讨论】:

        【解决方案18】:

        这对我有用。它写入一个仅包含所有文件名称(无路径)的文件。然后它会读取该 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;
                }
        
            }
        

        【讨论】:

          【解决方案19】:

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

          【讨论】:

            【解决方案20】:

            由于一个目录的文件和子目录一般都是以树形结构存储的,一个直观的方法是使用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);
            }
            

            【讨论】:

              【解决方案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;
              }
              

              【讨论】:

                【解决方案22】:

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

                1. 从 github 页面下载 dirent.h 文件。但是最好只使用 Raw dirent.h 文件并按照下面的步骤操作(这就是我让它工作的方式)。

                  用于 Windows 的 dirent.h 的 Github 页面:Github page for dirent.h

                  原始 Dirent 文件:Raw dirent.h File

                2. 转到您的项目并添加一个新项目 (Ctrl+Shift+A)。添加头文件(.h)并将其命名为dirent.h。

                3. 将Raw dirent.h File 代码粘贴到您的标题中。

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

                5. 将下面的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);
                  }
                  

                【讨论】:

                  【解决方案23】:

                  我尝试按照bothanswers 中给出的示例进行操作,可能值得注意的是,std::filesystem::directory_entry 似乎已更改为没有&lt;&lt; 运算符的重载。而不是std::cout &lt;&lt; p &lt;&lt; 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 &lt;&lt; 导致丢失重载错误。

                  【讨论】:

                    【解决方案24】:

                    我想分享一些东西,并感谢您的阅读材料。玩弄一下这个函数来理解它。你可能会喜欢。 e 代表扩展,p 代表路径,s 代表路径分隔符。

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

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

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

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

                    如果您只读取文件不多的目录,则节省可能不会太多。但是,如果您正在读取大量目录,或者该目录有几十万个文件,则可能会节省大量资金。

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

                    【讨论】:

                      【解决方案25】:

                      以 herohuyongtao 发布的内容和其他一些帖子为基础:

                      http://www.cplusplus.com/forum/general/39766/

                      What is the expected input type of FindFirstFile?

                      How to convert wstring into string?

                      这是一个 Windows 解决方案。

                      因为我想传入 std::string 并返回一个字符串向量,所以我必须进行几次转换。

                      #include <string>
                      #include <Windows.h>
                      #include <vector>
                      #include <locale>
                      #include <codecvt>
                      
                      std::vector<std::string> listFilesInDir(std::string path)
                      {
                          std::vector<std::string> names;
                          //Convert string to wstring
                          std::wstring search_path = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(path);
                          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)) 
                                  {
                                      //convert from wide char to narrow char array
                                      char ch[260];
                                      char DefChar = ' ';
                                      WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, ch, 260, &DefChar, NULL);
                                      names.push_back(ch);
                                  }
                              } 
                              while (::FindNextFile(hFind, &fd));
                              ::FindClose(hFind);
                          }
                          return names;
                      }
                      

                      【讨论】:

                      • 如果您知道您将只使用多字节,您可以使用WIN32_FIND_DATAA、FindFirstFileA 和FindNextFileA。那么就不需要将结果转换为多字节或将输入转换为unicode。
                      • 只是建议:std::wstring_convert 已弃用(几年前现在)。如果您使用各种英语的操作系统,也许this might be a good enough replacement, .. 除了那个字符串向量,我假设使用 c++ 异常,是最大和最慢解决方案的可靠方法。除非您使用一些非常好的标准库替代品...
                      【解决方案26】:
                      #include<iostream>
                      #include <dirent.h>
                      using namespace std;
                      char ROOT[]={'.'};
                      
                      void listfiles(char* path){
                          DIR * dirp = opendir(path);
                          dirent * dp;
                          while ( (dp = readdir(dirp)) !=NULL ) {
                               cout << dp->d_name << " size " << dp->d_reclen<<std::endl;
                          }
                          (void)closedir(dirp);
                      }
                      
                      int main(int argc, char **argv)
                      {
                          char* path;
                          if (argc>1) path=argv[1]; else path=ROOT;
                      
                          cout<<"list files in ["<<path<<"]"<<std::endl;
                          listfiles(path);
                      
                          return 0;
                      }
                      

                      【讨论】:

                        【解决方案27】:

                        从dirent.h 尝试scandir()

                        man scandir()

                        【讨论】:

                          【解决方案28】:

                          根据以上答案

                          #include <vector>
                          #include <string>
                          #include <algorithm>
                          
                          #ifdef _WIN32
                          #include <windows.h>
                          std::vector<std::string> files_in_directory(std::string path)
                          {
                              std::vector<std::string> files;
                          
                              // check directory exists
                              char fullpath[MAX_PATH];
                              GetFullPathName(path.c_str(), MAX_PATH, fullpath, 0);
                              std::string fp(fullpath);
                              if (GetFileAttributes(fp.c_str()) != FILE_ATTRIBUTE_DIRECTORY)
                                  return files;
                          
                              // get file names
                              WIN32_FIND_DATA findfiledata;
                              HANDLE hFind = FindFirstFile((LPCSTR)(fp + "\\*").c_str(), &findfiledata);
                              if (hFind != INVALID_HANDLE_VALUE)
                              {
                                  do 
                                  {
                                      files.push_back(findfiledata.cFileName);
                                  } 
                                  while (FindNextFile(hFind, &findfiledata));
                                  FindClose(hFind);
                              }
                          
                              // delete current and parent directories
                              files.erase(std::find(files.begin(), files.end(), "."));
                              files.erase(std::find(files.begin(), files.end(), ".."));
                          
                              // sort in alphabetical order
                              std::sort(files.begin(), files.end());
                          
                              return files;
                          }
                          #else
                          #include <dirent.h>
                          std::vector<std::string> files_in_directory(std::string directory)
                          {
                              std::vector<std::string> files;
                          
                              // open directory
                              DIR *dir;
                              dir = opendir(directory.c_str());
                              if (dir == NULL)
                                  return files;
                          
                              // get file names
                              struct dirent *ent;
                              while ((ent = readdir(dir)) != NULL)
                                  files.push_back(ent->d_name);
                              closedir(dir);
                          
                              // delete current and parent directories
                              files.erase(std::find(files.begin(), files.end(), "."));
                              files.erase(std::find(files.begin(), files.end(), ".."));
                          
                              // sort in alphabetical order
                              std::sort(files.begin(), files.end());
                          
                              return files;
                          }
                          #endif  // _WIN32
                          

                          【讨论】:

                          • 对于 C++17,我们应该使用 std::filesystem::directory_iterator 和类似的。
                          • @0xC0000022L 当然。对于那些不支持 c++17 的人来说,这是一个跨平台的解决方案。
                          • 这几乎不是跨平台的。单独的 Windows 实现并不能说明 _UNICODE 的定义。除此之外,面对非常大的目录中的用户,这会爆炸。为什么大多数(底层)API 已经基于迭代器模型而不是一次获取一个巨大的列表,这是有原因的。也就是说,这当然是一个开始。但坦率地说,我可能会重写 Windows 部分,使其表现得像 readdir() 和朋友一样,因为这意味着一个比您提供的更灵活的单一界面。
                          • @0xC0000022L 感谢您的反馈。我在我的小项目中使用了这段代码,文件不多,平台要么是Windows,要么是Ubuntu。代码不属于我。 (我应该参考来源。)这是大多数情况下的简单解决方案。我发布了这个以供以后参考并与其他人分享。由于如今 C++17 被广泛使用,因此不再需要这篇文章。但是,如果您认为在没有第三方库的情况下保留非现代解决方案是个好主意,我鼓励您发布一个新答案,在这种情况下我将删除这个答案。
                          【解决方案29】:
                          #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;
                          }
                          

                          【讨论】:

                            【解决方案30】:

                            Shreevardhan 的设计也非常适合遍历子目录:

                            #include <string>
                            #include <iostream>
                            #include <filesystem>
                            
                            using namespace std;
                            namespace fs = filesystem;
                            int main()
                            {
                                string path = "\\path\\to\\directory";
                                // string path = "/path/to/directory";
                                for (auto & p : fs::recursive_directory_iterator(path))
                                    cout << p.path() << endl;
                            }
                            

                            编译:cl /EHsc /W4 /WX /std:c++17 ListFiles.cpp

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2011-09-11
                              • 1970-01-01
                              • 2010-09-23
                              • 1970-01-01
                              相关资源
                              最近更新 更多