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


593

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

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


7
Это дубликат 609236
Крис


1
@chrish - Да, но у этого есть классическое "Я не могу выполнить 'ls'"! Это именно то, что я чувствую на первом курсе информатики. ; D <3 x
Джеймс Бедфорд

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

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

Ответы:


810

В небольших и простых задачах я не использую 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;
}

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

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

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

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

10
С C ++ 14 есть std::experimental::filesystem, с C ++ 17 есть std::filesystem. Смотрите ответ Shreevardhan ниже. Так что нет необходимости в сторонних библиотеках.
Рой Дантон

347

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


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

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

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

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

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

229

К сожалению, стандарт C ++ не определяет стандартный способ работы с файлами и папками таким образом.

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

Кроссплатформенный метод повышения:

Следующая функция, учитывая путь к каталогу и имя файла, рекурсивно ищет в каталоге и его подкаталогах имя файла, возвращая bool, а в случае успеха - путь к найденному файлу.

bool find_file(const path & dir_path,         // in this directory,
               const std::string & file_name, // search for this name,
               path & path_found)             // placing path here if found
{
    if (!exists(dir_path)) 
        return false;

    directory_iterator end_itr; // default construction yields past-the-end

    for (directory_iterator itr(dir_path); itr != end_itr; ++itr)
    {
        if (is_directory(itr->status()))
        {
            if (find_file(itr->path(), file_name, path_found)) 
                return true;
        }
        else if (itr->leaf() == file_name) // see below
        {
            path_found = itr->path();
            return true;
        }
    }
    return false;
}

Источник со страницы поддержки, упомянутой выше.

Для систем на основе 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.


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

7
С C ++ 14 есть std::experimental::filesystem, с C ++ 17 есть std::filesystem, у которых есть подобная функциональность как boost (библиотеки получены из boost). Смотрите ответ Shreevardhan ниже.
Рой Дантон

Для окон, обратитесь к docs.microsoft.com/en-us/windows/desktop/FileIO/… для подробностей
FindOutIslamNow

90

Одной функции достаточно, вам не нужно использовать какую-либо стороннюю библиотеку (для 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-файлы (т.е. определенного типа) в этом каталоге.


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

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

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

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

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

51

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

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

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

33

Я рекомендую использовать 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("./*");

2
Проверьте, что glob () возвращает ноль.
Камиль Гудесюн

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

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

Является ли это кроссплатформенным совместимым?
Нихил Августин

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

23

Вот очень простой код 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

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

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

23

Почему бы не использовать 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

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

Это хорошо, когда вы знаете файл, который вы ищете, например * .txt
Кемин Чжоу

20

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

#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 */
};

Я хотел бы этот.
Самостоятельная загрузка

10

Попробуйте повысить для метода X-платформы

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

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


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

@ ice1000 Серьезно? Это Q & A от 2009
Тим

8

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

};

6

GNU Manual FTW

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

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

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

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

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


5

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

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

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

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

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

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

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/");
}

2

вы можете получить все прямые файлы в вашем корневом каталоге, используя 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;
}

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

1

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

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

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

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


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

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

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 <<привела к отсутствующей ошибке перегрузки.


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

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

0

Просто то, чем я хочу поделиться и поблагодарить вас за материал для чтения. Поиграйте с функцией, чтобы немного ее понять. Вам может понравиться это. e обозначает расширение, p обозначает путь, а s обозначает разделитель пути.

Если путь пропущен без конечного разделителя, разделитель будет добавлен к пути. Для расширения, если введена пустая строка, функция вернет любой файл, который не имеет расширения в своем имени. Если была введена одна звезда, будут возвращены все файлы в каталоге. Если длина e больше 0, но не является единственной *, то точка e будет добавлена ​​к 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;
}

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

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

    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.