How can I get the list of files in a directory using C or C++?
如何从C或C++代码中确定目录中的文件列表?
我不允许执行
在小而简单的任务中,我不使用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; } |
另外,
不幸的是,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 & 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;
}
来源于上面提到的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 && !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], &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所提到的,您可以将
对于纯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,所以即使是非常古老的编译器也可以使用它
我建议在这个可重用的包装器中使用
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("./*"); |
下面是
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 |
为什么不使用
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的此类。只需通过提供您想要列表的
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"。
在代码中放入下面的
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); } |
我试着遵循两个答案中给出的例子,可能值得注意的是,似乎
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; } |
试图将
这对我有用。它只使用所有文件的名称(没有路径)写入文件。然后它读取该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; } } |