Как я могу получить список файлов в каталоге, используя C или C++?
Как я могу определить список файлов в каталоге из моего кода C или C++?
Мне не разрешено исполнять ls
команда и разобрать результаты в моей программе.
33 ответа
В небольших и простых задачах я не использую 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!).
Автор слоя совместимости окон - Тони Ронкко. В 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 & p : fs::directory_iterator(path))
std::cout << p << std::endl; // "p" is the directory entry. Get the path with "p.path()".
}
Также, std::filesystem::recursive_directory_iterator
также может перебирать подкаталоги.
К сожалению, стандарт C++ не определяет стандартный способ работы с файлами и папками таким образом.
Поскольку кроссплатформенного способа не существует, лучший кроссплатформенный способ - это использовать библиотеку, например модуль файловой системы boost.
Кроссплатформенный метод повышения:
Следующая функция, учитывая путь к каталогу и имя файла, рекурсивно ищет в каталоге и его подкаталогах имя файла, возвращая 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;
}
Источник со страницы поддержки, упомянутой выше.
Для систем на основе Unix/Linux:
Вы можете использовать opendir / readdir / closedir.
Пример кода, который ищет в каталоге запись ``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;
Исходный код с вышеупомянутых man-страниц.
Для систем на базе 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.
Одной функции достаточно, вам не нужно использовать какую-либо стороннюю библиотеку (для 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 only, пожалуйста, проверьте это. Требуется только дополнительный заголовок:
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("./*");
Я думаю, ниже фрагмент может быть использован для перечисления всех файлов.
#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 */
};
Вот очень простой код в 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;
}
Попробуйте повысить для метода X-платформы
http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm
или просто используйте файлы, специфичные для вашей ОС.
Проверьте этот класс, который использует 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;
}
};
#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;
}
GNU Manual FTW
http://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html
Кроме того, иногда хорошо идти прямо к источнику (каламбур). Вы можете многому научиться, взглянув на внутренности некоторых наиболее распространенных команд в Linux. Я установил простое зеркало GNU Coreutils на GitHub (для чтения).
https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c
Может быть, это не относится к Windows, но с помощью этих методов может быть несколько случаев использования вариантов Unix.
Надеюсь, это поможет...
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;
}
Я надеюсь, что этот код поможет вам.
#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;
}
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;
}
Эта реализация реализует вашу цель, динамически заполняя массив строк содержимым указанного каталога.
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;
}
Системный вызов это!
system( "dir /b /s /a-d * > file_names.txt" );
Тогда просто прочитайте файл.
РЕДАКТИРОВАТЬ: Этот ответ следует считать взломать, но он действительно работает (хотя и в зависимости от платформы), если у вас нет доступа к более элегантным решениям.
Этот ответ должен работать для пользователей Windows, которые столкнулись с трудностями при работе с Visual Studio с любыми другими ответами.
Загрузите файл dirent.h со страницы github. Но лучше просто использовать файл Raw dirent.h и следовать моим инструкциям ниже (именно так я и получил).
Страница Github для dirent.h для Windows: Страница Github для dirent.h
Файл Raw Dirent: Файл Raw dirent.h
Перейдите к своему проекту и добавьте новый элемент (Ctrl + Shift + A). Добавьте заголовочный файл (.h) и назовите его dirent.h.
Вставьте код файла Raw dirent.h в ваш заголовок.
Включите "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); }
Я попытался следовать примеру, приведенному в обоих ответах, и, возможно, стоит отметить, что это выглядит так, как будто 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 <<
привело к отсутствующей ошибке перегрузки.
Вы можете получить все прямые файлы в вашем корневом каталоге, используя std:: эксперимент:: 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;
}
Дизайн Шривардхана также отлично подходит для обхода подкаталогов:
#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
Это работает для меня. Извините, если не могу вспомнить источник. Это, вероятно, со страницы руководства.
#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/");
}
Решение Питера Паркера, но без использования for:
#include <algorithm>
#include <filesystem>
#include <ranges>
#include <vector>
using namespace std;
int main() {
vector<filesystem::path> filePaths;
ranges::transform(filesystem::directory_iterator("."),
back_inserter(filePaths), [](const auto& dirFile){return dirFile.path();} );
}
#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;
}
Поскольку файлы и подкаталоги каталога обычно хранятся в древовидной структуре, интуитивно понятным способом является использование алгоритма DFS для рекурсивного обхода каждого из них. Вот пример в операционной системе Windows с использованием основных файловых функций в io.h. Вы можете заменить эти функции на другой платформе. Что я хочу выразить, так это то, что основная идея 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);
}
Основываясь на том, что herohuyongtao опубликовал и несколько других сообщений:
http://www.cplusplus.com/forum/general/39766/
Каков ожидаемый тип ввода FindFirstFile?
Как конвертировать wstring в строку?
Это решение для 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;
}
Просто в Linux используйте следующий код стиля ASCI C
#include <bits/stdc++.h>
#include <dirent.h>
using namespace std;
int main(){
DIR *dpdf;
struct dirent *epdf;
dpdf = opendir("./");
if (dpdf != NULL){
while (epdf = readdir(dpdf)){
cout << epdf->d_name << std::endl;
}
}
closedir(dpdf);
return 0;
}
Надеюсь это поможет!
Основываясь на ответах выше
#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