Як я можу отримати список файлів у каталозі за допомогою C або C ++?


593

Як я можу визначити список файлів у каталозі з мого коду C або C ++?

Мені заборонено виконувати lsкоманду та аналізувати результати в межах моєї програми.


7
Це дублікат 609236
chrish


1
@chrish - Так, але в цьому є класика "Мені заборонено виконувати" ls "! Саме так я почував себе на 1 курсі інформатики. ; D <3 x
Джеймс Бедфорд

1
C і C ++ - це не одна і та ж мова. Тому процедура виконання цього завдання буде різною в обох мовах. Виберіть його та повторно позначте відповідно.
MD XF

2
І жодна з цих мов (крім C ++, оскільки C ++ 17) навіть не має поняття каталогів, тому будь-яка відповідь, ймовірно, залежатиме від вашої ОС або від будь-яких бібліотек абстракцій, якими ви можете користуватися.
Toby Speight

Відповіді:


809

У невеликих і простих завданнях я не використовую 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 (без образи, мені подобається прискорення!).

Автор шару сумісності з Windows - Тоні Ронко. У 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 для Windows. Я думаю, що це особистий смак, але dirent.h більше відчуває себе як стандарт
Пітер Паркер

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

42
люди стають реальні !! це питання з 2009 року, і він навіть не згадував В.С. Тому не критикуйте, що ваш повний фірмовий (хоча і досить приємний) IDE не підтримує багатовікові стандарти ОС. Також у моїй відповіді сказано, що вона "доступна" для Windows, не "включена" в будь-яку IDE відтепер і на всі часи ... Я впевнений, що ви можете завантажити Dirent і помістити його в деякі включити "dir" та "voila".
Пітер Паркер

6
Відповідь вводить в оману. Слід почати з: " ... я використовую dirent.h , для якого також існує шар сумісності з відкритим кодом для Windows ".
rustyx

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

347

C ++ 17 тепер має a 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
При переході std::filesystem::pathдо std::coutлапки, лапки включаються у висновок. Щоб уникнути цього, додайте .string()шлях до явного замість неявного перетворення (тут std::cout << p.string() << std::endl;). Приклад: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton

2
Що з символами NON-ASCII у назвах файлів? Не std::wstringслід використовувати або який тип у ітераторі?
anddero

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

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;

Вихідний код із вказаних вище сторінок.

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

Для Windows див. 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, але ОП ніколи не просить мати рішення для платформ. До речі, я завжди вважаю за краще щось вибирати, не використовуючи бібліотеки 3rd (якщо можливо).
herohuyongtao

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

1
@herohuyongtao OP згадував, що він не може розібрати ls, це означає, що він, ймовірно, на unix .. все одно, хороша відповідь для Windows.
Томас

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

51

Що стосується рішення лише для С, будь ласка, ознайомтесь із цим. Потрібен лише додатковий заголовок:

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>відповідні шляхи до файлів, які відповідають глобальному шаблону:

#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. Чи можете ви сказати мені, як вирішити це питання?
Тофув

Зауважте, що ця рутина заглиблюється лише на один рівень (рекурсії немає). Він також не робить швидку перевірку , щоб визначити , чи є це файл або каталог, який ви можете легко зробити шляхом перемикання GLOB_TILDEз , GLOB_TILDE | GLOB_MARKа потім перевірка шляхів , що закінчуються косою рисою. Вам доведеться внести будь-які зміни до нього, якщо вам це потрібно.
Воломійка

Чи сумісна ця кросплатформа?
Нікхіл Августин

На жаль, ви не можете знайти рівномірно приховані файли через 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
@Alexander De Leon: Ви можете отримати цю бібліотеку на їхньому веб-сайті boost.org , прочитайте спочатку посібник для роботи, а потім скористайтеся їх boost::filesystemбібліотекою boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Bad

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

Це може бути кращою відповіддю, якщо ви поясніть необхідні включення.
Воломіке

2
Перевірте, що glob () повертає нуль!
orbitcowboy

Це добре, коли ви знаєте, який файл ви шукаєте, наприклад * .txt
Kemin Zhou

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 {
    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 Серйозно? Питання та відповіді - з 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 FTW

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

Крім того, іноді добре поїхати прямо до джерела (призначений каламбур). Можна багато чого навчитися, переглянувши внутрішні частини деяких найпоширеніших команд в Linux. Я встановив просте дзеркало ядер 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 :: експериментальний :: файлова система :: 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

    Невикористовуваний файл-файли-файли: Сирий файл 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" );

Потім просто прочитайте файл.

EDIT: Цю відповідь слід вважати хакерською, але вона справді спрацьовує (хоча і певним чином), якщо у вас немає доступу до більш елегантних рішень.


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 не містила крапки в нульовому положенні.

Для повертається значення. Якщо повернути нульову карту, то нічого не знайдено, але каталог був відкритий. Якщо індекс 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

Це працювало для мене. Він записує файл із лише іменами (без шляху) усіх файлів. Потім він читає той файл txt і друкує його для вас.

void DisplayFolderContent()
    {

        system("dir /n /b * > file_names.txt");
        char ch;
        std::fstream myStream("file_names.txt", std::fstream::in);
        while (myStream.get(ch))
        {
            std::cout << ch;
        }

    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.