如何使用 C 或 C++ 获取目录中的文件列表?
题
如何从 C 或 C++ 代码中确定目录中的文件列表?
我无权执行 ls
命令并解析我的程序中的结果。
解决方案
在小而简单的任务中,我不使用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中,它是标准标题。
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现在有一个 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
也可以迭代子目录。
不幸的是,C++ 标准没有定义以这种方式处理文件和文件夹的标准方法。
由于没有跨平台的方式,最好的跨平台方式是使用诸如 增强文件系统模块.
跨平台提升方法:
以下函数在给定目录路径和文件名的情况下,递归地搜索目录及其子目录中的文件名,返回 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; }
来源自上述 boost 页面。
对于基于 Unix/Linux 的系统:
在目录中搜索条目“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;
上述手册页中的源代码。
对于基于 Windows 的系统:
您可以使用 Win32 API 查找第一个文件 / 查找下一个文件 / 查找关闭 功能。
以下 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 页面。
一个功能就足够了,您不需要使用任何第三方库(适用于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文件(即特定类型)。
对于仅 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
如果可用,这意味着它(通常)是线程安全的 - 通过相同的方式支持 Windows UTF-16
UNICODE
宏 - 它是 C90,所以即使是非常古老的编译器也可以使用它
我建议使用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("./*");
这是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
为什么不使用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;
}
我认为,下面的代码段可以用来列出所有文件。
#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 */
};
查看这个使用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;
}
};
GNU手册FTW
另外,有时候直接来源(双关语)是好的。通过查看Linux中一些最常见命令的内部结构,您可以学到很多东西。我在github上设置了一个简单的GNU coreutils镜像(用于阅读)。
https://github.com/homer6/gnu_coreutils/blob/master/src/ls .C
也许这不能解决Windows问题,但是使用这些方法可以获得许多使用Unix变种的案例。
希望有帮助...
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;
}
我希望这段代码可以帮助你。
#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;
}
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;
}
此实现实现了您的目的,使用指定目录的内容动态填充字符串数组。
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;
}
这对我有用。如果我不记得来源,我很抱歉。它可能来自手册页。
#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/");
}
您可以使用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;
}
系统调用它!
system( "dir /b /s /a-d * > file_names.txt" );
然后只需阅读文件。
编辑:这个答案应该被认为是一个黑客攻击,但如果您无法访问更优雅的解决方案,它确实可行(尽管以特定于平台的方式)。由于目录的文件和子目录通常以树结构存储,因此直观的方法是使用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);
}
此答案适用于使用Visual Studio以及其他任何答案进行此操作时遇到问题的Windows用户。
-
从github页面下载dirent.h文件。但最好只使用Raw dirent.h文件并按照下面的步骤操作(这是我如何使用它)。
针对Windows的dirent.h的Github页面:针对dirent的Github页面.H
Raw Dirent File: Raw dirent.h文件
-
转到您的项目并添加新项目( Ctrl + Shift + A )。添加头文件(.h)并将其命名为dirent.h。
-
粘贴 Raw dirent.h文件将代码添加到标题中。
-
包括<!> quot; dirent.h <!> quot;在你的代码中。
-
将以下
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); }
醇>
我尝试按照两者中给出的示例答案,可能值得注意的是,似乎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 <<
导致缺少重载错误。
我想分享一些内容并感谢您阅读材料。玩这个功能有点了解它。你可能会喜欢它。 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;
}
这对我有用。它只写入所有文件的名称(无路径)的文件。然后它会读取该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;
}
}