Как я могу определить список файлов в каталоге из моего кода C или C ++?
Мне не разрешено выполнять команду ls
и анализировать результаты из моей программы.
Как я могу определить список файлов в каталоге из моего кода C или C ++?
Мне не разрешено выполнять команду ls
и анализировать результаты из моей программы.
ОБНОВЛЕНИЕ 2017 г. :
В C ++ 17 теперь есть официальный способ перечисления файлов вашей файловой системы: std::filesystem
. Ниже приведен отличный ответ от Шривардхана с этим исходным кодом (этот код может вызывать ошибки):
#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, а также доступен для 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;
}
Это всего лишь небольшой файл заголовка, который выполняет большую часть простых действий, которые вам нужны, без использования большого подхода на основе шаблонов, такого как ускорение (без обид, я люблю ускорение!).
Мне понравился tinydir на этот ответ: coderhelper.com/a/14679177/901641 Я предпочитаю более простые загрузки (не давайте мне список вариантов, дайте мне правильный выбор. Честно говоря, C / C ++ уже достаточно усложнил навигацию по каталогам уже; зачем мне возиться с этим?)
@ArtOfWarfare: tinydir даже не был создан, когда на этот вопрос был дан ответ. Также это оболочка для dirent (POSIX) и FindFirstFile (Windows), а dirent.h просто обертка для dirent для окон. Я думаю, что это личный вкус, но dirent.h кажется более стандартным.
@Peter Parker - Я просто хотел поддержать этот ответ, так как он еще не получил столько внимания, и из немногих здесь, которые я пробовал, он был моим любимым.
Почему это не приводит к утечке памяти? Я понимаю, что closedir (dir) очистит * dir, но как насчет * ent?
@JoshC: потому что * ent - это просто возвращаемый указатель внутреннего представления. закрыв каталог, вы также удалите * ent. Поскольку * ent предназначен только для чтения, я думаю, это нормальный дизайн.
Обратите внимание, что readdir НЕ гарантирует порядок чтения файлов. См. coderhelper.com/questions/8977441/…
Не входит в состав Windows по умолчанию, поэтому его не очень удобно переносить. coderhelper.com/questions/5530933/…
@JoshC по той же причине fopen
не создает утечку памяти в паре с fclose
.
<dirent.h> не существует на VS15.
Мои два цента: я поместил этот код в класс C ++ здесь
Не теряйте время - в MSVC нет <dirent.h>
, opendir
и т. Д.! Я не понимаю, почему люди голосуют за этот ответ
люди становятся настоящими !! это вопрос из 2009 года, и в нем даже не упоминается VS. Так что не критикуйте, что ваша полностью проприетарная (хотя и довольно приятная) IDE не поддерживает стандарты ОС вековой давности. Также в моем ответе говорилось, что он «доступен» для окон, а не «включен» в любую среду IDE с этого момента и на все времена ... Я почти уверен, что вы можете загрузить dirent и поместить его в какой-то каталог include, и вот он.
@rustyx Потому что он (а) предоставляет ссылку на уровень совместимости для Windows (б) ни разу не сказал, что решение работает из коробки на всех машинах везде (в) работает
Ответ вводит в заблуждение. Он должен начинаться с: « ... Я использую dirent.h , для которого также существует уровень совместимости с открытым исходным кодом Windows ».
это работает, но если я покажу имена файлов, найденных в каталоге, я получу "." и ".." в списке файлов. почему так? могу ли я отредактировать код, чтобы исключить эти параметры? Мне нужно перебрать список файлов, и это может быть проблемой
Следует упомянуть, что для этого требуются #include <dirent.h>
, #include <stdio.h>
и #include <stdlib.h>
.
Я пытаюсь включить dirent.h, но получаю ошибку - can't open source file
В C ++ 14 есть std::experimental::filesystem
, в C ++ 17 - std::filesystem
. См. Ответ Шривардхана ниже. Так что нет необходимости в сторонних библиотеках.
он не поддерживает путь к файлу, закодированный с помощью GBK.
Я думаю, что для использования решения С ++ 17 требуется std :: cout << p.path (). String () вместо std :: cout << p ?!
Как я могу исключить "." и ".." (текущий и предыдущий каталог) из результата метода 1?
просто сравните строки ..?
Не могли бы вы переместить «Обновление 2017» в начало ответа? Было бы лучше, если бы этот вопрос был найден через поиск или по ссылке.
@peterchen хороший момент .. подойдет .. надеюсь, он не нарушает никаких правил
Вклад в ответ Сезара Алехандро Монтеро Ороско.
Красота заключается в простоте, и, добавляя ключ /s
, мы также просматриваем подкаталоги.
system("dir /n /b /s * > file_names.txt");
Дизайн Шривардхана также отлично подходит для перемещения по подкаталогам:
#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 <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;
}
На основании ответов выше
#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
#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;
}
Основываясь на том, что опубликовал 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;
}
Если вы знаете, что будете использовать только многобайтовые файлы, вы можете использовать WIN32_FIND_DATAA
, FindFirstFileA
и FindNextFileA
. Тогда не будет необходимости преобразовывать результат в многобайтовый или ввод в юникод.
Просто совет: std::wstring_convert
устарел (несколько лет назад). если вы используете ОС на каком-либо английском языке, возможно, , это может быть достаточно хорошей заменой, .. кроме этого вектора строк, и я предполагаю, что с использованием исключений C ++, это верный путь к самое большое и самое медленное решение. если вы не используете некоторые из немногих очень хороших замен std lib ...
Просто кое-что, чем я хочу поделиться и поблагодарить вас за материалы для чтения. Поиграйте с функцией, чтобы понять ее. Вам это может понравиться. 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;
}
Я попытался следовать примеру, приведенному в обоих ответах, и, возможно, стоит отметить, что похоже, что 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 <<
привела к отсутствующей ошибке перегрузки.
Этот ответ должен работать для пользователей 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 :: 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;
}
Поскольку файлы и подкаталоги каталога обычно хранятся в древовидной структуре, интуитивно понятным способом является использование алгоритма 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);
}
Ответ Шривардхана отлично работает. Но если вы хотите использовать его в С ++ 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;
}
У меня это сработало. Он записывает файл только с именами (без пути) всех файлов. Затем он читает этот текстовый файл и распечатывает его для вас.
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;
}
}
Это работает для меня. Извините, если я не могу вспомнить источник. Вероятно, это страница руководства.
#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/");
}
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)
Просто имейте в виду, что текущая реализация recursive_directory_iterator и _Directory_iterator пропускает каталоги, которые он не может открыть из-за проблем с разрешениями.
Учтите, что при передаче std::filesystem::path
в std::cout
кавычки включаются в вывод. Чтобы этого избежать, добавьте к пути .string()
, чтобы выполнить явное преобразование вместо неявного (здесь std::cout << p.string() << std::endl;
). Пример: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
А как насчет символов, отличных от ASCII, в именах файлов? Не следует использовать std::wstring
или какой тип от итератора?
Как поместить путь к файлу в "p" в строку (или wstring)? Во всех примерах я обнаружил случаи ошибок при компиляции с Visual Studio 2017. Например «ошибка C2039: 'строка': не является членом 'std :: experimental :: filesystem :: v1 :: directory_entry'» для «p. string () "и" ошибка C3867: 'std :: experimental :: filesystem :: v1 :: directory_entry :: path': нестандартный синтаксис; используйте '&' для создания указателя на член "для" wstring path = p .дорожка;".
@ BjörnLarsson Пример в моем комментарии выше является неполным, поскольку p
внутри ответа - это directory_iterator
вместо пути (запутался этим, поскольку термин p
обычно используется для std::filesystem::path
извините). p.path().string()
для узких струн, соответственно p.path().wstring()
для широких струн, должно работать.
Чтобы исправить себя еще раз, p
в ответе будет directory_entry
.
Я не уверен, что я один в этом, но без ссылки на -lstdc++fs
я бы получил SIGSEGV (Address boundary error)
. Я не нашел нигде в документации, что это было необходимо, да и компоновщик не дал никаких подсказок. Это сработало как для g++ 8.3.0
, так и для clang 8.0.0-3
. Кто-нибудь знает, где такие вещи указаны в документации / спецификациях?
Для C ++ 14 измените заголовок на #include <experimental/filesystem>
Фактически, без флага "-lstdc ++ fs" я даже не смог бы скомпилировать (collect2: error: ld returned 1 exit status
). Бьюсь об заклад, это необходимо, потому что я использую <filesystem>
в C ++ 14, который предоставляет его в качестве экспериментальной функции.
Вот очень простой код в 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
@ Плохо, как бы мне изменить это, чтобы выводить полный каталог для каждого файла. как я хочу D: /AnyFolder/file1.txt и так далее?
Эта реализация реализует вашу цель, динамически заполняя массив строк содержимым указанного каталога.
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);
Я рекомендую использовать glob
с этой многоразовой оболочкой. Он генерирует vector<string>
, соответствующий путям файлов, которые соответствуют шаблону глобуса:
#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
.
Системный вызов!
system( "dir /b /s /a-d * > file_names.txt" );
Тогда просто прочтите файл.
РЕДАКТИРОВАТЬ: этот ответ следует рассматривать как взлом, но он действительно работает (хотя и зависит от платформы), если у вас нет доступа к более элегантным решениям.
Мне не разрешено выполнять команду ls и анализировать результаты из моей программы. Я знал, что найдется кто-нибудь, кто пришлет что-то подобное ...
Для Windows это, безусловно, наиболее прагматичный способ. Обратите особое внимание на переключатель /A
. Какой бы способ вы ни выбрали, безопасность здесь может серьезно помешать. Если не «кодировать» с самого начала. Подражание Windows, аутентификация и другие «пустяки» никогда не бывает легким делом.
Почему бы не использовать 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.
Достаточно одной функции, вам не нужно использовать какие-либо сторонние библиотеки (для 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-файлы (т.е. определенного типа) в этом каталоге.
Это решение, если оно зависит от платформы. Вот почему вам нужны сторонние библиотеки.
@kraxor Да, это работает только в Windows, но OP никогда не просит кроссплатформенное решение. Кстати, я всегда предпочитаю выбирать что-то без использования сторонних библиотек (если это возможно).
@herohuyongtao OP никогда не указывал платформу, и предоставление решения общего вопроса, сильно зависящего от платформы, может ввести в заблуждение. (Что, если есть однострочное решение, которое работает только на PlayStation 3? Это хороший ответ?) Я вижу, вы отредактировали свой ответ, указав, что оно работает только в Windows, я думаю, это нормально.
@kraxor Я думаю, что это должен быть хороший ответ, если вы можете написать его одной строкой для конкретной платформы, но с использованием языка, указанного OP, то есть C / C ++.
@herohuyongtao Что ж, я думаю, что хороший ответ - «не существует решения« C / C ++ », но вот как это сделать на <platform>». Или что-то вдоль этих линий. Во всяком случае, это только я.
@herohuyongtao OP упомянул, что он не может разбирать ls, что означает, что он, вероятно, использует unix ... в любом случае, хороший ответ для Windows.
Вы должны упомянуть, что папка должна заканчиваться символом \, и если вам нужны только специальные файлы, вы можете изменить . на, например, * .dll, и вы получите только DLL-файлы в этом каталоге. Это действительно здорово, но только для Windows ;-).
@Sebastian Спасибо за заметку и соответственно уточнил ответ.
Самый распространенный ответ с <dirent.h> также относится к платформе * nix. Я использую окна, поэтому этот ответ более актуален для меня, поэтому +1. :-)
Почему sprintf
, а не функция c++
? Кроме того, почему бы не использовать двухстороннюю очередь вместо вектора для вставки O (1)?
Кроме того, как отправить ему текущий каталог проекта?
В итоге я использовал std::vector<std::wstring>
, а затем fileName.c_str()
вместо вектора строк, который не компилировался.
Есть ли способ получить несколько расширений? Например, * .exe и * .dll по одному и тому же пути.
здорово ! Я изменяю только [Набор символов] в настройке проекта Visual Studio на «Не задано», и это работает хорошо.
Шаблон подстановки *.*
будет извлекать все файлы, в имени которых есть хотя бы одна точка. В Windows правильный шаблон для получения всех файлов - это просто *
. MS-DOS требовала *.*
для получения всех файлов, и похоже, что эта привычка просто прижилась. Поскольку в большинстве имен файлов обычно есть точка, иногда это просто не удается, и никто этого не замечает.
В каком порядке эта функция вернула имена файлов.? Если нет порядка, есть ли способ отсортировать имена по дате, как в проводнике Windows сортировать по дате?
Думаю, приведенный ниже фрагмент можно использовать для перечисления всех файлов.
#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 и т. Д. Хорошее решение!
Это было здорово! В каком порядке я должен получать файлы?
Для решения только на 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);
Некоторые преимущества перед другими вариантами:
readdir_r
там, где это возможно, что означает (обычно) потокобезопасный UNICODE
Очень хорошее предложение. Я еще не тестировал его на компьютере с Windows, но он отлично работает на OS X.
Библиотека не поддерживает std :: string, поэтому вы не можете передать file.c_str () в tinydir_open. В этом случае он выдает ошибку C2664 во время компиляции на msvc 2015.
Руководство GNU FTW
Кроме того, иногда полезно сразу перейти к источнику (каламбур). Вы можете многому научиться, заглянув внутрь некоторых из наиболее распространенных команд в Linux. Я установил простое зеркало GNU coreutils на github (для чтения).
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;
}
-1. string2wchar_t
возвращает адрес локальной переменной. Кроме того, вам, вероятно, следует использовать методы преобразования, доступные в WinAPI, вместо того, чтобы писать свои собственные.
Обратите внимание на этот класс, который использует 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;
}
};
Где вы закроете ручку?
К сожалению, стандарт C ++ не определяет стандартный способ работы с файлами и папками таким образом.
Поскольку кросс-платформенный способ отсутствует, лучший кроссплатформенный способ - использовать библиотеку, такую как модуль файловой системы boost.
Метод повышения кроссплатформенности:
Следующая функция, учитывая путь к каталогу и имя файла, рекурсивно ищет в каталоге и его подкаталогах имя файла, возвращая логическое значение и, в случае успеха, путь к найденному файлу.
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 <60.
В следующем примере 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\\*"), &findFileData);
В C ++ 14 есть std::experimental::filesystem
, в C ++ 17 есть std::filesystem
, которые имеют такую же функциональность, что и boost (библиотеки получены из boost). См. Ответ Шривардхана ниже.
Для окон см. docs.microsoft.com/en-us/windows/desktop/FileIO/… для получения дополнительных сведений.
Попробуйте форсировать метод x-platform
http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm
или просто используйте файлы, специфичные для вашей ОС.
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из обзора
@ ice1000 Серьезно? Эти вопросы и ответы относятся к 2009 году.
Это дубликат 609236
См. Также
stat()
ошибка «нет такого файла или каталога», когда имя файла возвращаетсяreaddir()
.@chrish - Да, но у этого есть классическая фраза «Мне не разрешено выполнять 'ls'»! Это именно , как я себя чувствую на первом курсе информатики. ; D <3 x
C и C ++ - это разные языки. Следовательно, процедура выполнения этой задачи будет разной для обоих языков. Пожалуйста, выберите один и пометьте его соответствующим образом.
И ни один из этих языков (кроме C ++, начиная с C ++ 17) даже не имеет концепции каталога, поэтому любой ответ, вероятно, будет зависеть от вашей ОС или любых библиотек абстракций, которые вы можете использовать.