Jak mogę uzyskać listę plików w katalogu przy użyciu C lub C ++?


592

Jak mogę ustalić listę plików w katalogu z mojego kodu C lub C ++?

Nie wolno mi wykonywać lspolecenia i analizować wyników z poziomu mojego programu.


7
To jest duplikat 609236
chrish


1
@chrish - Tak, ale ten ma klasyczne „Nie wolno mi wykonywać„ ls ”! To dokładnie jak czułbym 1 roku informatyki. ; D <3 x
James Bedford

1
C i C ++ nie są tym samym językiem. Dlatego procedura wykonania tego zadania będzie różna w obu językach. Wybierz jeden i odpowiednio ponownie otaguj.
MD XF,

2
I żaden z tych języków (inny niż C ++ od C ++ 17) nie ma nawet pojęcia katalogu - więc każda odpowiedź prawdopodobnie będzie zależała od twojego systemu operacyjnego lub bibliotek abstrakcyjnych, których możesz używać.
Toby Speight

Odpowiedzi:


809

W małych i prostych zadaniach nie używam doładowania, używam dirent.h, który jest również dostępny dla systemu 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;
}

Jest to tylko mały plik nagłówkowy i wykonuje większość prostych czynności, których potrzebujesz, bez korzystania z dużego podejścia opartego na szablonie, takiego jak boost (bez obrazy, lubię boost!).

Autorem warstwy zgodności z systemem Windows jest Toni Ronkko. W Uniksie jest to standardowy nagłówek.

AKTUALIZACJA 2017 :

W C ++ 17 jest teraz oficjalnym sposób do plików wykazu systemu plików: std::filesystem. Poniżej znajduje się doskonała odpowiedź Shreevardhan z tego kodu źródłowego:

#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 nie został nawet utworzony, gdy udzielono odpowiedzi na to pytanie. Jest to również opakowanie wokół dirent (POSIX) i FindFirstFile (Windows), podczas gdy dirent.h po prostu otacza dirent dla Windows. Myślę, że to osobisty gust, ale dirent.h czuje się bardziej standardem
Peter Parker

7
@JoshC: ponieważ * ent jest tylko zwróconym wskaźnikiem wewnętrznej reprezentacji. przez zamknięcie katalogu wyeliminujesz również * ent. Ponieważ * ent jest tylko do czytania, myślę, że jest to rozsądny projekt.
Peter Parker

42
ludzie stają się prawdziwi !! to pytanie z 2009 roku i nawet nie wspomniało o VS. Więc nie krytykuj, że twoje pełne IDE (choć całkiem miłe) IDE nie obsługuje wielowiekowych standardów systemu operacyjnego. Również moja odpowiedź mówi, że jest ona „dostępna” dla systemu Windows, a nie „dołączona” w żadnym IDE od teraz i na zawsze ... Jestem prawie pewna, że ​​możesz pobrać dirent i wstawić go do niektórych programów, takich jak dir i voila.
Peter Parker,

6
Odpowiedź jest myląca. Powinien zaczynać się od: „ ... używam dirent.h , dla którego istnieje również warstwa kompatybilności z otwartym oprogramowaniem Windows ”.
rustyx

10
W C ++ 14 jest std::experimental::filesystem, w C ++ 17 jest std::filesystem. Zobacz odpowiedź Shreevardhan poniżej. Więc nie ma potrzeby bibliotek zewnętrznych.
Roi Danton

346

C ++ 17 ma teraz std::filesystem::directory_iterator, którego można używać jako

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

Ponadto, std::filesystem::recursive_directory_iteratormożna iteracyjne podkatalogi również.


27
AFAIK może być również stosowany w C ++ 14, ale to wciąż eksperymentalna: namespace fs = std::experimental::filesystem;. Wygląda na to, że działa dobrze.
PeterK,

7
To powinna być preferowana odpowiedź dla bieżącego użycia (zaczynając od C ++ 17)
dioda zielona

4
Podczas przekazywania std::filesystem::pathdo std::coutcudzysłowu są uwzględniane w danych wyjściowych. Aby tego uniknąć, dołącz .string()do ścieżki, aby wykonać jawną zamiast niejawnej konwersji (tutaj std::cout << p.string() << std::endl;). Przykład: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton

2
Co ze znakami NIE-ASCII w nazwach plików? Nie std::wstringnależy używać lub jaki jest typ z iteratora?
anddero

2
Nie jestem pewien, czy jestem w tym sam, ale bez linkowania -lstdc++fs, dostałbym SIGSEGV (Address boundary error). Nigdzie w dokumentacji nie mogłem znaleźć, że jest to wymagane, a linker nie dał mi żadnej wskazówki. To działało zarówno dla, jak g++ 8.3.0i dla clang 8.0.0-3. Czy ktoś ma jakiś wgląd w to, gdzie takie rzeczy są określone w dokumentach / specyfikacjach?
swalog

229

Niestety standard C ++ nie definiuje standardowego sposobu pracy z plikami i folderami w ten sposób.

Ponieważ nie ma metody wieloplatformowej, najlepszym sposobem wieloplatformowym jest użycie biblioteki, takiej jak moduł systemu plików doładowania .

Metoda doładowania między platformami:

Poniższa funkcja, podając ścieżkę do katalogu i nazwę pliku, rekursywnie przeszukuje katalog i jego podkatalogi w poszukiwaniu nazwy pliku, zwracając wartość bool i, jeśli się powiedzie, ścieżkę do znalezionego pliku.

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

Źródło ze strony doładowania wspomnianej powyżej.

W systemach opartych na Unix / Linux:

Możesz użyć opendir / readdir / closedir .

Przykładowy kod przeszukujący katalog pod kątem wpisu `` nazwa '' to:

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;

Kod źródłowy z powyższych stron podręcznika.

W przypadku systemów opartych na systemie Windows:

Możesz użyć funkcji Win32 API FindFirstFile / FindNextFile / FindClose .

Poniższy przykład C ++ pokazuje minimalne użycie 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);
   }
}

Kod źródłowy z powyższych stron msdn.


Zastosowanie:FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);
Константин Ван

7
W C ++ 14 istnieje std::experimental::filesystem, w C ++ 17 istnieje std::filesystem, który ma podobną funkcjonalność jak boost (biblioteki lib pochodzą z boost). Zobacz odpowiedź Shreevardhan poniżej.
Roi Danton

W przypadku systemu Windows szczegółowe informacje można znaleźć na stronie docs.microsoft.com/en-us/windows/desktop/FileIO/ ...
FindOutIslamNow,

90

Wystarczy jedna funkcja, nie musisz używać żadnej biblioteki innej firmy (dla systemu 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: jak wspomniano w @Sebastian, możesz zmienić *.*na *.ext, aby uzyskać tylko pliki EXT (tj. Określonego typu) w tym katalogu.


20
To rozwiązanie jest specyficzne dla platformy. Dlatego potrzebujesz bibliotek innych firm.
kraxor

9
@kraxor Tak, działa tylko w systemie Windows, ale OP nigdy nie prosi o rozwiązanie dla wielu platform. BTW, zawsze wolę wybierać coś bez korzystania z 3. bibliotek (jeśli to możliwe).
herohuyongtao

7
@herohuyongtao OP nigdy nie określił platformy, a podanie silnie zależnego od platformy rozwiązania ogólnego pytania może być mylące. (Co jeśli istnieje jedno-liniowe rozwiązanie, które działa tylko na PlayStation 3? Czy to dobra odpowiedź tutaj?) Widzę, że zredagowałeś swoją odpowiedź, aby stwierdzić, że działa ona tylko w systemie Windows, myślę, że w ten sposób jest w porządku.
kraxor

1
@herohuyongtao OP wspomniał, że nie może parsować ls, co oznacza, że ​​prawdopodobnie jest na Uniksie. W każdym razie dobra odpowiedź dla Windows.
Thomas

2
Skończyło się na użyciu a, std::vector<std::wstring>a następnie fileName.c_str()zamiast wektora ciągów, których nie można skompilować.
PerryC,

51

Sprawdź tylko rozwiązanie typu C. Wymaga tylko dodatkowego nagłówka:

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

Niektóre zalety w stosunku do innych opcji:

  • Jest przenośny - otacza POSIX dirent i Windows FindFirstFile
  • Używa readdir_rtam, gdzie jest dostępny, co oznacza, że ​​jest (zwykle) bezpieczny dla wątków
  • Obsługuje system Windows UTF-16 za pośrednictwem tych samych UNICODEmakr
  • Jest to C90, więc mogą go używać nawet bardzo starożytne kompilatory

2
Bardzo miła sugestia. Nie przetestowałem go jeszcze na komputerze z systemem Windows, ale działa znakomicie w systemie OS X.
ArtOfWarfare

Biblioteka nie obsługuje std :: string, więc nie możesz przekazać file.c_str () do tinydir_open. W tym przypadku generuje błąd C2664 podczas kompilacji na msvc 2015.
Stepan Yakovenko

33

Polecam używać globz tym opakowaniem wielokrotnego użytku. Generuje vector<string>odpowiadające ścieżki plików, które pasują do wzorca globu:

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

Które można następnie wywołać za pomocą normalnego systemu symboli wieloznacznych, takiego jak:

vector<string> files = globVector("./*");

2
Sprawdź, czy glob () zwraca zero.
Camille Goudeseune,

Chciałbym użyć glob.h zgodnie z zaleceniami. Ale nadal nie mogę dołączyć pliku .h: Mówi No such file or directory. Czy możesz mi powiedzieć, jak rozwiązać ten problem?
Tofuw

Zauważ, że ta procedura przechodzi tylko jeden poziom w głąb (bez rekurencji). Nie sprawdza też szybko, czy jest to plik czy katalog, co można łatwo zrobić, przełączając GLOB_TILDEsię, GLOB_TILDE | GLOB_MARKa następnie sprawdzając ścieżki kończące się ukośnikiem. W razie potrzeby musisz wprowadzić dowolną modyfikację.
Volomike,

Czy to jest kompatybilne z wieloma platformami?
Nikhil Augustine

Niestety nie można znaleźć jednolicie ukrytych plików glob.
LmTinyToon,

23

Oto bardzo prosty kod przy C++11użyciu boost::filesystembiblioteki do uzyskiwania nazw plików w katalogu (z wyjątkiem nazw folderów):

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

Wyjście jest jak:

file1.txt
file2.dat

Cześć, a gdzie mogę dostać tę bibliotekę?
Alexander Leon VI

2
@Alexander De Leon: Możesz pobrać tę bibliotekę na ich stronie boost.org , najpierw przeczytaj przewodnik dla początkujących, a następnie skorzystaj z ich boost::filesystembiblioteki boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Zły

23

Dlaczego nie użyć 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;
}

To może być lepsza odpowiedź, jeśli wyjaśnisz wymagane elementy.
Volomike

2
Sprawdź, czy glob () zwraca zero!
orbitcowboy

Jest to dobre, gdy znasz plik, którego szukasz, np. * .Txt
Kemin Zhou

20

Myślę, że poniżej fragmentu można użyć do wyświetlenia wszystkich plików.

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

Poniżej znajduje się struktura struktury 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 */
};

Chciałbym ten
selfboot

10

Wypróbuj boost dla metody X-platform

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

lub po prostu użyj plików specyficznych dla systemu operacyjnego.


Chociaż ten link może odpowiedzieć na pytanie, lepiej dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
ice1000

@ ice1000 Poważnie? Pytania i odpowiedzi pochodzą z 2009 r.
Tim

8

Sprawdź tę klasę, która korzysta z interfejsu API win32. Wystarczy skonstruować instancję, podając foldernameżądaną pozycję, a następnie wywołać getNextFilemetodę, aby uzyskać następną filenamez katalogu. Myślę, że potrzebuje windows.hi 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

Podręcznik GNU FTW

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

Czasami dobrze jest przejść od razu do źródła (zamierzona gra słów). Możesz się wiele nauczyć, patrząc na niektóre z najczęściej używanych poleceń w Linuksie. Założyłem proste zwierciadło podstawowych plików GNU na github (do czytania).

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

Być może nie dotyczy to systemu Windows, ale przy użyciu tych metod można mieć wiele przypadków użycia wariantów uniksowych.

Mam nadzieję, że pomoże ...


5

Odpowiedź Shreevardhan działa świetnie. Ale jeśli chcesz go użyć w c ++ 14, po prostu dokonaj zmianynamespace fs = experimental::filesystem;

to znaczy,

#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

Mam nadzieję, że ten kod ci pomoże.

#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_tzwraca adres zmiennej lokalnej. Powinieneś także prawdopodobnie użyć metod konwersji dostępnych w WinAPI zamiast pisać własne.
Daniel Kamil Kozar

3

Ta implementacja realizuje Twój cel, dynamicznie wypełniając tablicę ciągów treścią określonego katalogu.

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

Jak mam to nazwać? Dostaję awarie, gdy próbuję uruchomić tę funkcję w pierwszym ifbloku. Nazywam to zchar **list; int numItems; exploreDirectory("/folder",list, numItems);
Hal T

2

To działa dla mnie. Przepraszam, jeśli nie pamiętam źródła. Prawdopodobnie pochodzi ze strony podręcznika man.

#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

możesz pobrać wszystkie bezpośrednie pliki z katalogu głównego, używając std :: experimental :: filesystem :: directory_iterator (). Następnie przeczytaj nazwę tych ścieżek.

#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

Ta odpowiedź powinna działać dla użytkowników systemu Windows, którzy mieli problemy z uzyskaniem pracy z Visual Studio z innymi odpowiedziami.

  1. Pobierz plik dirent.h ze strony github. Ale lepiej jest po prostu użyć pliku Raw dirent.h i postępować zgodnie z poniższymi krokami (tak mam go uruchomić).

    Strona Github dla dirent.h dla Windows: Strona Github dla dirent.h

    Plik Raw Dirent : Plik Raw dirent.h

  2. Przejdź do swojego projektu i dodaj nowy przedmiot ( Ctrl+ Shift+ A). Dodaj plik nagłówka (.h) i nazwij go dirent.h.

  3. Wklej kod pliku Raw dirent.h do nagłówka.

  4. Dołącz „dirent.h” do swojego kodu.

  5. Umieść poniższą void filefinder()metodę w swoim kodzie i wywołaj ją z mainfunkcji lub edytuj funkcję, jak chcesz jej używać.

    #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 nazwij to!

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

Następnie po prostu przeczytaj plik.

EDYCJA: Tę odpowiedź należy uznać za włamanie, ale naprawdę działa (choć w sposób specyficzny dla platformy), jeśli nie masz dostępu do bardziej eleganckich rozwiązań.


7
Nie wolno mi wykonywać polecenia „ls” i analizować wyników z mojego programu. Wiedziałem, że będzie ktoś, kto wyśle ​​coś takiego ...
yyny

1

Ponieważ pliki i podkatalogi katalogu są zwykle przechowywane w strukturze drzewa, intuicyjnym sposobem jest użycie algorytmu DFS do rekurencyjnego przechodzenia między nimi. Oto przykład w systemie operacyjnym Windows przy użyciu podstawowych funkcji plików w io.h. Możesz zastąpić te funkcje na innej platformie. Chcę wyrazić, że podstawowa idea DFS doskonale spełnia ten problem.

#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

Próbowałem podążać za przykładem podanym w obu odpowiedziach i być może warto zauważyć, że wygląda na to, że std::filesystem::directory_entryzostał zmieniony tak, aby nie miał przeciążenia <<operatora. Zamiast tego std::cout << p << std::endl;musiałem użyć następujących, aby móc skompilować i uruchomić:

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

próba przejścia psama, std::cout <<powodując błąd braku przeładowania.


1

Opierając się na tym, co opublikował herohuyongtao i kilka innych postów:

http://www.cplusplus.com/forum/general/39766/

Jaki jest oczekiwany typ danych wejściowych FindFirstFile?

Jak przekonwertować ciąg na ciąg?

To jest rozwiązanie dla systemu Windows.

Ponieważ chciałem przekazać std :: string i zwrócić wektor ciągów, musiałem dokonać kilku konwersji.

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

Jeśli wiesz, że będziesz używać tylko wielobajtów, których możesz użyć WIN32_FIND_DATAA, FindFirstFileAi FindNextFileA. Wtedy nie będzie potrzeby konwertowania wyniku na wielobajtowy lub danych wejściowych na Unicode.
Kiran Thilak,

0

Po prostu coś, czym chcę się podzielić i dziękuję za lekturę. Pobaw się z tą funkcją, aby ją trochę zrozumieć. Może ci się spodobać e oznacza rozszerzenie, p oznacza ścieżkę, a s oznacza separator ścieżki.

Jeśli ścieżka zostanie przekazana bez kończącego separatora, do ścieżki zostanie dołączony separator. W przypadku rozszerzenia, jeśli zostanie wprowadzony pusty ciąg, funkcja zwróci dowolny plik, który nie ma rozszerzenia w nazwie. Jeśli wprowadzono pojedynczą gwiazdkę, wszystkie pliki w katalogu zostaną zwrócone. Jeśli długość e jest większa niż 0, ale nie jest pojedynczą *, kropka zostanie dodana do e, jeśli e nie zawierało kropki w pozycji zerowej.

Dla wartości zwracanej. Jeśli zostanie zwrócona mapa o zerowej długości, nic nie zostanie znalezione, ale katalog jest otwarty. Jeśli indeks 999 jest dostępny z wartości zwracanej, ale rozmiar mapy wynosi tylko 1, oznacza to, że wystąpił problem z otwarciem ścieżki katalogu.

Należy pamiętać, że w celu zwiększenia wydajności funkcję tę można podzielić na 3 mniejsze funkcje. Ponadto możesz utworzyć funkcję wywołującą, która wykryje, którą funkcję wywoła na podstawie danych wejściowych. Dlaczego to jest bardziej wydajne? Powiedziane, jeśli masz zamiar pobrać wszystko, co jest plikiem, wykonując tę ​​metodę podfunkcja zbudowana do pobierania wszystkich plików po prostu pobierze wszystkie pliki i nie będzie musiała oceniać żadnego innego niepotrzebnego warunku za każdym razem, gdy znajdzie plik.

Dotyczy to także pobierania plików, które nie mają rozszerzenia. Specjalna wbudowana funkcja do tego celu oceniałaby tylko pod kątem pogody, czy znaleziony obiekt jest plikiem, a następnie czy nazwa pliku zawiera kropkę.

Oszczędność może nie być duża, jeśli czytasz tylko katalogi z mniejszą ilością plików. Ale jeśli czytasz dużą liczbę katalogów lub jeśli katalog zawiera kilkaset tysięcy plików, może to być ogromna oszczędność.

#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

To zadziałało dla mnie. Zapisuje plik zawierający tylko nazwy (bez ścieżki) wszystkich plików. Następnie odczytuje ten plik txt i drukuje go dla ciebie.

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

    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.