Как я могу получить список файлов в каталоге с помощью C или C ++?

avatar
samoz
4 марта 2009 в 19:35
1039547
31
676

Как я могу определить список файлов в каталоге из моего кода C или C ++?

Мне не разрешено выполнять команду ls и анализировать результаты из моей программы.

Источник
chrish
4 марта 2009 в 20:35
8

Это дубликат 609236

James Bedford
22 октября 2016 в 11:50
4

@chrish - Да, но у этого есть классическая фраза «Мне не разрешено выполнять 'ls'»! Это именно , как я себя чувствую на первом курсе информатики. ; D <3 x

MD XF
2 марта 2017 в 21:58
4

C и C ++ - это разные языки. Следовательно, процедура выполнения этой задачи будет разной для обоих языков. Пожалуйста, выберите один и пометьте его соответствующим образом.

Toby Speight
20 февраля 2018 в 11:38
3

И ни один из этих языков (кроме C ++, начиная с C ++ 17) даже не имеет концепции каталога, поэтому любой ответ, вероятно, будет зависеть от вашей ОС или любых библиотек абстракций, которые вы можете использовать.

Ответы (31)

avatar
Peter Parker
4 марта 2009 в 19:57
914

ОБНОВЛЕНИЕ 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;
}

Это всего лишь небольшой файл заголовка, который выполняет большую часть простых действий, которые вам нужны, без использования большого подхода на основе шаблонов, такого как ускорение (без обид, я люблю ускорение!).

ArtOfWarfare
18 мая 2013 в 23:19
0

Мне понравился tinydir на этот ответ: coderhelper.com/a/14679177/901641 Я предпочитаю более простые загрузки (не давайте мне список вариантов, дайте мне правильный выбор. Честно говоря, C / C ++ уже достаточно усложнил навигацию по каталогам уже; зачем мне возиться с этим?)

Peter Parker
20 мая 2013 в 19:13
7

@ArtOfWarfare: tinydir даже не был создан, когда на этот вопрос был дан ответ. Также это оболочка для dirent (POSIX) и FindFirstFile (Windows), а dirent.h просто обертка для dirent для окон. Я думаю, что это личный вкус, но dirent.h кажется более стандартным.

ArtOfWarfare
20 мая 2013 в 23:01
1

@Peter Parker - Я просто хотел поддержать этот ответ, так как он еще не получил столько внимания, и из немногих здесь, которые я пробовал, он был моим любимым.

Josh C
24 сентября 2014 в 14:04
1

Почему это не приводит к утечке памяти? Я понимаю, что closedir (dir) очистит * dir, но как насчет * ent?

Peter Parker
25 сентября 2014 в 11:41
9

@JoshC: потому что * ent - это просто возвращаемый указатель внутреннего представления. закрыв каталог, вы также удалите * ent. Поскольку * ent предназначен только для чтения, я думаю, это нормальный дизайн.

King Crimson
29 апреля 2015 в 04:25
1

Обратите внимание, что readdir НЕ гарантирует порядок чтения файлов. См. coderhelper.com/questions/8977441/…

Joe
9 октября 2015 в 18:51
1

Не входит в состав Windows по умолчанию, поэтому его не очень удобно переносить. coderhelper.com/questions/5530933/…

user253751
22 ноября 2015 в 08:15
0

@JoshC по той же причине fopen не создает утечку памяти в паре с fclose.

shinzou
5 декабря 2015 в 16:20
2

<dirent.h> не существует на VS15.

Martín Coll
14 декабря 2015 в 23:29
1

Мои два цента: я поместил этот код в класс C ++ здесь

rustyx
15 апреля 2016 в 08:48
4

Не теряйте время - в MSVC нет <dirent.h>, opendir и т. Д.! Я не понимаю, почему люди голосуют за этот ответ

Peter Parker
15 апреля 2016 в 09:43
55

люди становятся настоящими !! это вопрос из 2009 года, и в нем даже не упоминается VS. Так что не критикуйте, что ваша полностью проприетарная (хотя и довольно приятная) IDE не поддерживает стандарты ОС вековой давности. Также в моем ответе говорилось, что он «доступен» для окон, а не «включен» в любую среду IDE с этого момента и на все времена ... Я почти уверен, что вы можете загрузить dirent и поместить его в какой-то каталог include, и вот он.

Fund Monica's Lawsuit
3 июля 2016 в 07:07
2

@rustyx Потому что он (а) предоставляет ссылку на уровень совместимости для Windows (б) ни разу не сказал, что решение работает из коробки на всех машинах везде (в) работает

rustyx
3 июля 2016 в 19:43
8

Ответ вводит в заблуждение. Он должен начинаться с: « ... Я использую dirent.h , для которого также существует уровень совместимости с открытым исходным кодом Windows ».

ubisum
24 сентября 2016 в 15:11
0

это работает, но если я покажу имена файлов, найденных в каталоге, я получу "." и ".." в списке файлов. почему так? могу ли я отредактировать код, чтобы исключить эти параметры? Мне нужно перебрать список файлов, и это может быть проблемой

FluorescentGreen5
13 октября 2016 в 07:24
0

Следует упомянуть, что для этого требуются #include <dirent.h>, #include <stdio.h> и #include <stdlib.h>.

STF
27 марта 2017 в 04:30
0

Я пытаюсь включить dirent.h, но получаю ошибку - can't open source file

Roi Danton
13 апреля 2017 в 07:49
13

В C ++ 14 есть std::experimental::filesystem, в C ++ 17 - std::filesystem. См. Ответ Шривардхана ниже. Так что нет необходимости в сторонних библиотеках.

Allen Hong
2 августа 2017 в 10:22
1

он не поддерживает путь к файлу, закодированный с помощью GBK.

Wolfgang
23 августа 2018 в 09:30
0

Я думаю, что для использования решения С ++ 17 требуется std :: cout << p.path (). String () вместо std :: cout << p ?!

Kamran Hosseini
16 ноября 2018 в 17:12
0

Как я могу исключить "." и ".." (текущий и предыдущий каталог) из результата метода 1?

Peter Parker
20 ноября 2018 в 12:54
1

просто сравните строки ..?

peterchen
4 ноября 2020 в 16:18
0

Не могли бы вы переместить «Обновление 2017» в начало ответа? Было бы лучше, если бы этот вопрос был найден через поиск или по ссылке.

Peter Parker
5 ноября 2020 в 15:09
1

@peterchen хороший момент .. подойдет .. надеюсь, он не нарушает никаких правил

avatar
jarikmesik
16 ноября 2021 в 16:50
0

Вклад в ответ Сезара Алехандро Монтеро Ороско.

Красота заключается в простоте, и, добавляя ключ /s, мы также просматриваем подкаталоги.

system("dir /n /b /s * > file_names.txt");
avatar
jarikmesik
16 ноября 2021 в 15:45
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

avatar
3ashry
14 января 2021 в 01:38
3
#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;
}
avatar
Burak
13 января 2021 в 15:59
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
avatar
user14347980
27 сентября 2020 в 16:03
0

Попробуйте scandir() из dirent.h

man scandir()

avatar
Stan Sokolov
10 декабря 2019 в 01:45
0
#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;
}
avatar
tzg
30 августа 2019 в 15:46
1

Основываясь на том, что опубликовал 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;
}
Kiran Thilak
6 декабря 2019 в 06:49
1

Если вы знаете, что будете использовать только многобайтовые файлы, вы можете использовать WIN32_FIND_DATAA, FindFirstFileA и FindNextFileA. Тогда не будет необходимости преобразовывать результат в многобайтовый или ввод в юникод.

Chef Gladiator
2 декабря 2020 в 11:49
0

Просто совет: std::wstring_convert устарел (несколько лет назад). если вы используете ОС на каком-либо английском языке, возможно, , это может быть достаточно хорошей заменой, .. кроме этого вектора строк, и я предполагаю, что с использованием исключений C ++, это верный путь к самое большое и самое медленное решение. если вы не используете некоторые из немногих очень хороших замен std lib ...

avatar
Kevin Ng
29 сентября 2018 в 10:54
0

Просто кое-что, чем я хочу поделиться и поблагодарить вас за материалы для чтения. Поиграйте с функцией, чтобы понять ее. Вам это может понравиться. 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;
}
avatar
StarKiller4011
25 сентября 2018 в 13:10
1

Я попытался следовать примеру, приведенному в обоих ответах, и, возможно, стоит отметить, что похоже, что 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 << привела к отсутствующей ошибке перегрузки.

avatar
ZKR
4 сентября 2018 в 00:24
2

Этот ответ должен работать для пользователей Windows, у которых возникли проблемы с тем, чтобы заставить его работать с Visual Studio с любым другим ответом.

  1. Загрузите файл dirent.h со страницы github. Но лучше просто использовать файл Raw dirent.h и следовать моим инструкциям ниже (так я заставил его работать).

    Страница Github для dirent.h для Windows: Страница Github для dirent.h

    Файл Raw Dirent: Файл Raw dirent.h

  2. Перейдите в свой проект и добавьте новый элемент ( Ctrl + Shift + A ). Добавьте файл заголовка (.h) и назовите его dirent.h.

  3. Вставьте код файла Raw dirent.h в свой заголовок.

  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);
    }
    
avatar
ducPham
20 апреля 2018 в 18:04
2

вы можете напрямую получить все файлы в корневом каталоге, используя 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;
}
avatar
tkingcer
10 апреля 2018 в 15:16
1

Поскольку файлы и подкаталоги каталога обычно хранятся в древовидной структуре, интуитивно понятным способом является использование алгоритма 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);
}
avatar
Venkat Vinay
13 марта 2018 в 06:38
6

Ответ Шривардхана отлично работает. Но если вы хотите использовать его в С ++ 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;
}
avatar
Cesar Alejandro Montero Orozco
28 июня 2017 в 05:23
-1

У меня это сработало. Он записывает файл только с именами (без пути) всех файлов. Затем он читает этот текстовый файл и распечатывает его для вас.

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

    }
avatar
ENHering
22 мая 2017 в 04:44
2

Это работает для меня. Извините, если я не могу вспомнить источник. Вероятно, это страница руководства.

#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/");
}
avatar
Shreevardhan
28 мая 2016 в 02:41
420

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 также может перебирать подкаталоги.

PeterK
11 июля 2016 в 08:03
36

AFAIK также можно использовать в C ++ 14, но там он все еще экспериментальный: namespace fs = std::experimental::filesystem;. Хотя вроде работает нормально.

green diod
3 января 2017 в 18:28
7

Это должен быть предпочтительный ответ для текущего использования (начиная с C ++ 17)

db_
3 января 2017 в 18:50
1

Просто имейте в виду, что текущая реализация recursive_directory_iterator и _Directory_iterator пропускает каталоги, которые он не может открыть из-за проблем с разрешениями.

Roi Danton
13 апреля 2017 в 09:04
4

Учтите, что при передаче std::filesystem::path в std::cout кавычки включаются в вывод. Чтобы этого избежать, добавьте к пути .string(), чтобы выполнить явное преобразование вместо неявного (здесь std::cout << p.string() << std::endl;). Пример: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3

Snackoverflow
19 января 2018 в 13:46
2

А как насчет символов, отличных от ASCII, в именах файлов? Не следует использовать std::wstring или какой тип от итератора?

Björn Larsson
19 февраля 2018 в 13:57
1

Как поместить путь к файлу в "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 .дорожка;".

Roi Danton
19 июля 2018 в 15:50
2

@ BjörnLarsson Пример в моем комментарии выше является неполным, поскольку p внутри ответа - это directory_iterator вместо пути (запутался этим, поскольку термин p обычно используется для std::filesystem::path извините). p.path().string() для узких струн, соответственно p.path().wstring() для широких струн, должно работать.

Roi Danton
19 июля 2018 в 16:01
0

Чтобы исправить себя еще раз, p в ответе будет directory_entry.

swalog
2 июля 2019 в 10:16
2

Я не уверен, что я один в этом, но без ссылки на -lstdc++fs я бы получил SIGSEGV (Address boundary error). Я не нашел нигде в документации, что это было необходимо, да и компоновщик не дал никаких подсказок. Это сработало как для g++ 8.3.0, так и для clang 8.0.0-3. Кто-нибудь знает, где такие вещи указаны в документации / спецификациях?

liorko
8 июля 2019 в 13:15
1

Для C ++ 14 измените заголовок на #include <experimental/filesystem>

Louis-Jacob Lebel
7 мая 2020 в 01:17
1

Фактически, без флага "-lstdc ++ fs" я даже не смог бы скомпилировать (collect2: error: ld returned 1 exit status). Бьюсь об заклад, это необходимо, потому что я использую <filesystem> в C ++ 14, который предоставляет его в качестве экспериментальной функции.

avatar
Bad
25 июня 2015 в 16:51
25

Вот очень простой код в 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 Leon VI
8 июля 2015 в 19:30
0

Привет, а где взять эту библиотеку?

Bad
8 июля 2015 в 20:02
2

@Alexander De Leon: Вы можете получить эту библиотеку на их сайте boost.org, сначала прочтите руководство по началу работы, затем используйте их boost::filesystem библиотеку boost.org/doc/libs/1_58_0/libs /filesystem/doc/index.htm

Areeb Muzaffar
19 апреля 2021 в 22:40
0

@ Плохо, как бы мне изменить это, чтобы выводить полный каталог для каждого файла. как я хочу D: /AnyFolder/file1.txt и так далее?

avatar
Giacomo Marciani
13 сентября 2014 в 15:08
3

Эта реализация реализует вашу цель, динамически заполняя массив строк содержимым указанного каталога.

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;
}
Hal T
21 февраля 2017 в 15:17
0

Как бы я это назвал? Когда я пытаюсь запустить эту функцию в первом блоке if, у меня возникают сбои. Я звоню по номеру char **list; int numItems; exploreDirectory("/folder",list, numItems);

avatar
Chris Redford
11 июля 2014 в 17:15
34

Я рекомендую использовать 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("./*");
Camille Goudeseune
21 мая 2015 в 16:51
2

Проверьте, что glob () возвращает ноль.

Tofuw
23 февраля 2016 в 10:36
0

Я хотел бы использовать glob.h, как вы рекомендовали. Но все же я не могу включить файл .h: он говорит: No such file or directory. Подскажите, пожалуйста, как решить эту проблему?

Volomike
15 мая 2016 в 17:16
0

Обратите внимание, что эта процедура идет только на один уровень (без рекурсии). Он также не выполняет быструю проверку, чтобы определить, является ли это файлом или каталогом, что вы можете легко сделать, переключив GLOB_TILDE на GLOB_TILDE | GLOB_MARK, а затем проверив пути, заканчивающиеся косой чертой. Вам придется внести в него любую модификацию, если вам это нужно.

Nikhil Augustine
1 июля 2018 в 07:11
0

Совместима ли эта кроссплатформенность?

LmTinyToon
5 октября 2018 в 11:30
0

К сожалению, вы не можете найти однородно скрытые файлы через glob.

avatar
Catalyst
3 июля 2014 в 15:40
3

Системный вызов!

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

Тогда просто прочтите файл.

РЕДАКТИРОВАТЬ: этот ответ следует рассматривать как взлом, но он действительно работает (хотя и зависит от платформы), если у вас нет доступа к более элегантным решениям.

yyny
26 апреля 2015 в 15:37
7

Мне не разрешено выполнять команду ls и анализировать результаты из моей программы. Я знал, что найдется кто-нибудь, кто пришлет что-то подобное ...

Chef Gladiator
2 декабря 2020 в 11:36
0

Для Windows это, безусловно, наиболее прагматичный способ. Обратите особое внимание на переключатель /A. Какой бы способ вы ни выбрали, безопасность здесь может серьезно помешать. Если не «кодировать» с самого начала. Подражание Windows, аутентификация и другие «пустяки» никогда не бывает легким делом.

avatar
Meekohi
10 февраля 2014 в 18:57
24

Почему бы не использовать 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;
}
Volomike
15 мая 2016 в 00:46
0

Это может быть лучшим ответом, если вы объясните необходимые включения.

orbitcowboy
20 мая 2016 в 18:58
2

Проверьте, что glob () возвращает ноль!

Kemin Zhou
21 июля 2016 в 17:35
0

Это хорошо, если вы знаете файл, который ищете, например * .txt.

avatar
herohuyongtao
30 декабря 2013 в 20:56
99

Достаточно одной функции, вам не нужно использовать какие-либо сторонние библиотеки (для 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
29 мая 2014 в 13:14
22

Это решение, если оно зависит от платформы. Вот почему вам нужны сторонние библиотеки.

herohuyongtao
29 мая 2014 в 14:13
11

@kraxor Да, это работает только в Windows, но OP никогда не просит кроссплатформенное решение. Кстати, я всегда предпочитаю выбирать что-то без использования сторонних библиотек (если это возможно).

kraxor
29 мая 2014 в 17:20
7

@herohuyongtao OP никогда не указывал платформу, и предоставление решения общего вопроса, сильно зависящего от платформы, может ввести в заблуждение. (Что, если есть однострочное решение, которое работает только на PlayStation 3? Это хороший ответ?) Я вижу, вы отредактировали свой ответ, указав, что оно работает только в Windows, я думаю, это нормально.

herohuyongtao
29 мая 2014 в 17:23
0

@kraxor Я думаю, что это должен быть хороший ответ, если вы можете написать его одной строкой для конкретной платформы, но с использованием языка, указанного OP, то есть C / C ++.

kraxor
29 мая 2014 в 17:26
0

@herohuyongtao Что ж, я думаю, что хороший ответ - «не существует решения« C / C ++ », но вот как это сделать на <platform>». Или что-то вдоль этих линий. Во всяком случае, это только я.

Thomas
18 сентября 2014 в 21:20
1

@herohuyongtao OP упомянул, что он не может разбирать ls, что означает, что он, вероятно, использует unix ... в любом случае, хороший ответ для Windows.

Sebastian
19 августа 2015 в 12:30
1

Вы должны упомянуть, что папка должна заканчиваться символом \, и если вам нужны только специальные файлы, вы можете изменить . на, например, * .dll, и вы получите только DLL-файлы в этом каталоге. Это действительно здорово, но только для Windows ;-).

herohuyongtao
19 августа 2015 в 14:48
1

@Sebastian Спасибо за заметку и соответственно уточнил ответ.

Joe
9 октября 2015 в 18:53
1

Самый распространенный ответ с <dirent.h> также относится к платформе * nix. Я использую окна, поэтому этот ответ более актуален для меня, поэтому +1. :-)

shinzou
5 декабря 2015 в 16:30
0

Почему sprintf, а не функция c++? Кроме того, почему бы не использовать двухстороннюю очередь вместо вектора для вставки O (1)?

shinzou
5 декабря 2015 в 16:32
0

Кроме того, как отправить ему текущий каталог проекта?

PerryC
12 июля 2016 в 15:38
2

В итоге я использовал std::vector<std::wstring>, а затем fileName.c_str() вместо вектора строк, который не компилировался.

hshantanu
16 апреля 2017 в 21:13
0

Есть ли способ получить несколько расширений? Например, * .exe и * .dll по одному и тому же пути.

bruce
22 января 2018 в 03:03
0

здорово ! Я изменяю только [Набор символов] в настройке проекта Visual Studio на «Не задано», и это работает хорошо.

Ferruccio
29 марта 2018 в 20:39
1

Шаблон подстановки *.* будет извлекать все файлы, в имени которых есть хотя бы одна точка. В Windows правильный шаблон для получения всех файлов - это просто *. MS-DOS требовала *.* для получения всех файлов, и похоже, что эта привычка просто прижилась. Поскольку в большинстве имен файлов обычно есть точка, иногда это просто не удается, и никто этого не замечает.

krishnakumarcn
6 сентября 2018 в 13:24
0

В каком порядке эта функция вернула имена файлов.? Если нет порядка, есть ли способ отсортировать имена по дате, как в проводнике Windows сортировать по дате?

avatar
Shrikant
23 октября 2013 в 06:36
28

Думаю, приведенный ниже фрагмент можно использовать для перечисления всех файлов.

#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 */
};
selfboot
28 апреля 2019 в 09:25
0

Я бы хотел эту.

Nav
12 декабря 2020 в 16:56
0

Это помогло мне в C ++ 11 без использования Boost и т. Д. Хорошее решение!

KansaiRobot
27 мая 2021 в 00:56
0

Это было здорово! В каком порядке я должен получать файлы?

avatar
congusbongus
4 февраля 2013 в 01:06
56

Для решения только на 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, поэтому его могут использовать даже очень древние компиляторы
ArtOfWarfare
18 мая 2013 в 23:15
2

Очень хорошее предложение. Я еще не тестировал его на компьютере с Windows, но он отлично работает на OS X.

Stepan Yakovenko
18 октября 2017 в 09:20
0

Библиотека не поддерживает std :: string, поэтому вы не можете передать file.c_str () в tinydir_open. В этом случае он выдает ошибку C2664 во время компиляции на msvc 2015.

avatar
Homer6
11 ноября 2012 в 21:35
6

Руководство GNU FTW

http://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html#Simple-Directory-Lister

Кроме того, иногда полезно сразу перейти к источнику (каламбур). Вы можете многому научиться, заглянув внутрь некоторых из наиболее распространенных команд в Linux. Я установил простое зеркало GNU coreutils на github (для чтения).

https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c

Возможно, это не относится к Windows, но с помощью этих методов можно получить ряд случаев использования вариантов Unix.

Надеюсь, что это поможет ...

avatar
JasonYen2205
29 марта 2012 в 21:12
4
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;
}
avatar
Yas
19 февраля 2012 в 18:22
3

Надеюсь, этот код вам поможет.

#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;
}
Daniel Kamil Kozar
23 октября 2013 в 07:30
4

-1. string2wchar_t возвращает адрес локальной переменной. Кроме того, вам, вероятно, следует использовать методы преобразования, доступные в WinAPI, вместо того, чтобы писать свои собственные.

avatar
robertvarga
8 апреля 2011 в 16:48
9

Обратите внимание на этот класс, который использует 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;
    }

};
Mayur
22 октября 2020 в 23:03
0

Где вы закроете ручку?

avatar
Brian R. Bondy
4 марта 2009 в 19:38
234

К сожалению, стандарт 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.

Константин Ван
11 августа 2016 в 00:47
0

Использование: FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);

Roi Danton
13 апреля 2017 в 07:51
8

В C ++ 14 есть std::experimental::filesystem, в C ++ 17 есть std::filesystem, которые имеют такую ​​же функциональность, что и boost (библиотеки получены из boost). См. Ответ Шривардхана ниже.

FindOutIslamNow
12 ноября 2018 в 13:43
0

Для окон см. docs.microsoft.com/en-us/windows/desktop/FileIO/… для получения дополнительных сведений.

avatar
Tim
4 марта 2009 в 19:37
11

Попробуйте форсировать метод x-platform

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

или просто используйте файлы, специфичные для вашей ОС.

ice1000
28 июня 2018 в 12:26
0

Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из обзора

Tim
28 июня 2018 в 13:30
0

@ ice1000 Серьезно? Эти вопросы и ответы относятся к 2009 году.