Wie kann ich die Liste der Dateien in einem Verzeichnis mit C oder C ++ abrufen?


593

Wie kann ich die Liste der Dateien in einem Verzeichnis in meinem C- oder C ++ - Code ermitteln?

Ich darf den lsBefehl nicht ausführen und die Ergebnisse in meinem Programm analysieren.


7
Dies ist ein Duplikat von 609236
Chrish


1
@chrish - Ja, aber dieser hat den Klassiker "Ich darf das 'ls' nicht ausführen"! Es ist genau , wie ich 1. Jahr der Informatik fühlen würde. ; D <3 x
James Bedford

1
C und C ++ sind nicht dieselbe Sprache. Daher ist das Verfahren zur Ausführung dieser Aufgabe in beiden Sprachen unterschiedlich. Bitte wählen Sie eine aus und markieren Sie sie entsprechend neu.
MD XF

2
Und keine dieser Sprachen (außer C ++ seit C ++ 17) hat überhaupt ein Konzept eines Verzeichnisses - daher hängt jede Antwort wahrscheinlich von Ihrem Betriebssystem oder von den Abstraktionsbibliotheken ab, die Sie möglicherweise verwenden.
Toby Speight

Antworten:


809

Bei kleinen und einfachen Aufgaben verwende ich keinen Boost, sondern dirent.h, das auch für Windows verfügbar ist:

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

Es ist nur eine kleine Header-Datei und erledigt die meisten einfachen Dinge, die Sie benötigen, ohne einen großen vorlagenbasierten Ansatz wie Boost zu verwenden (nichts für ungut, ich mag Boost!).

Der Autor der Windows-Kompatibilitätsebene ist Toni Ronkko. In Unix ist es ein Standardheader.

UPDATE 2017 :

In C ++ 17 gibt es jetzt eine offizielle Möglichkeit, Dateien Ihres Dateisystems aufzulisten : std::filesystem. Es gibt eine ausgezeichnete Antwort von Shreevardhan unten mit diesem Quellcode:

#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 wurde nicht einmal erstellt, als diese Frage beantwortet wurde. Es ist auch ein Wrapper um dirent (POSIX) und FindFirstFile (Windows), während dirent.h nur dirent für Windows umschließt. Ich denke, es ist ein persönlicher Geschmack, aber dirent.h fühlt sich eher als Standard an
Peter Parker

7
@JoshC: weil * ent nur ein zurückgegebener Zeiger der internen Darstellung ist. Wenn Sie das Verzeichnis schließen, wird auch das * ent entfernt. Da das * ent nur zum Lesen gedacht ist, ist dies ein vernünftiges Design, denke ich.
Peter Parker

42
Leute werden echt !! Dies ist eine Frage aus dem Jahr 2009 und es wurde nicht einmal VS erwähnt. Kritisieren Sie also nicht, dass Ihre vollständig proprietäre (wenn auch recht nette) IDE keine jahrhundertealten Betriebssystemstandards unterstützt. Meine Antwort besagte auch, dass es für Windows "verfügbar" ist und ab sofort und für alle Zeiten in keiner IDE "enthalten" ist. Ich bin mir ziemlich sicher, dass Sie dirent herunterladen und in ein include-Verzeichnis und voila einfügen können, da es ist.
Peter Parker

6
Die Antwort ist irreführend. Es sollte beginnen mit: " ... ich verwende dirent.h , für das es auch eine Windows Open Source-Kompatibilitätsschicht gibt ".
Rustyx

10
Mit C ++ 14 gibt es std::experimental::filesystem, mit C ++ 17 gibt es std::filesystem. Siehe Antwort von Shreevardhan unten. Es werden also keine Bibliotheken von Drittanbietern benötigt.
Roi Danton

347

C ++ 17 hat jetzt eine std::filesystem::directory_iterator, die als verwendet werden kann

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

Auch std::filesystem::recursive_directory_iteratorkann iterieren Unterverzeichnisse als auch.


28
AFAIK kann auch in C ++ 14 verwendet werden, ist dort aber noch experimentell : namespace fs = std::experimental::filesystem;. Es scheint aber in Ordnung zu funktionieren.
PeterK

7
Dies sollte die bevorzugte Antwort für die aktuelle Verwendung sein (beginnend mit C ++ 17)
grüne Diode

4
Heed beim Passieren std::filesystem::pathzu std::cout, werden die Anführungszeichen in der Ausgabe enthalten. Um dies zu vermeiden, hängen Sie .string()an den Pfad an, um eine explizite anstelle einer impliziten Konvertierung durchzuführen (hier std::cout << p.string() << std::endl;). Beispiel: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton

2
Was ist mit NON-ASCII-Zeichen in Dateinamen? Sollte nicht std::wstringverwendet werden oder was ist der Typ vom Iterator?
Anddero

2
Ich bin mir nicht sicher, ob ich alleine bin, aber ohne Link zu -lstdc++fswürde ich eine bekommen SIGSEGV (Address boundary error). Ich konnte nirgendwo in der Dokumentation feststellen, dass dies erforderlich war, und der Linker gab auch keinen Hinweis. Dies funktionierte sowohl für g++ 8.3.0als auch clang 8.0.0-3. Hat jemand einen Einblick, wo solche Dinge in den Dokumenten / Spezifikationen angegeben sind?
Swalog

229

Leider definiert der C ++ - Standard keine Standardmethode für die Arbeit mit Dateien und Ordnern auf diese Weise.

Da es keinen plattformübergreifenden Weg gibt, ist es am besten, eine Bibliothek wie das Boost-Dateisystemmodul zu verwenden .

Plattformübergreifende Boost-Methode:

Die folgende Funktion durchsucht das Verzeichnis und seine Unterverzeichnisse unter Angabe eines Verzeichnispfads und eines Dateinamens rekursiv nach dem Dateinamen und gibt einen Bool und bei Erfolg den Pfad zu der gefundenen Datei zurück.

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

Quelle von der oben erwähnten Boost-Seite.

Für Unix / Linux-basierte Systeme:

Sie können opendir / readdir / closedir verwenden .

Beispielcode, der ein Verzeichnis nach dem Eintrag "Name" durchsucht, lautet:

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;

Quellcode aus den obigen Manpages.

Für ein Windows-basiertes System:

Sie können die Win32-API-Funktionen FindFirstFile / FindNextFile / FindClose verwenden.

Das folgende C ++ - Beispiel zeigt Ihnen eine minimale Verwendung von 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);
   }
}

Quellcode von den obigen msdn-Seiten.


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

7
Mit C ++ 14 gibt es std::experimental::filesystem, mit C ++ 17 gibt std::filesystemes ähnliche Funktionen wie boost (die Bibliotheken werden von boost abgeleitet). Siehe Antwort von Shreevardhan unten.
Roi Danton


90

Eine Funktion reicht aus, Sie müssen keine Bibliothek eines Drittanbieters (für Windows) verwenden.

#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: Wie von @Sebastian erwähnt, können Sie zu wechseln *.*, *.extum nur die EXT-Dateien (dh eines bestimmten Typs) in diesem Verzeichnis abzurufen .


20
Diese Lösung ist plattformspezifisch. Aus diesem Grund benötigen Sie Bibliotheken von Drittanbietern.
Kraxor

9
@kraxor Ja, es funktioniert nur unter Windows, aber OP verlangt nie nach einer plattformübergreifenden Lösung. Übrigens bevorzuge ich immer etwas ohne Verwendung von 3rd-Libraries (wenn möglich).
Herohuyongtao

7
@herohuyongtao OP hat nie eine Plattform angegeben, und es kann irreführend sein, eine stark plattformabhängige Lösung für eine allgemeine Frage anzugeben. (Was ist, wenn es eine einzeilige Lösung gibt, die nur auf PlayStation 3 funktioniert? Ist das hier eine gute Antwort?) Ich sehe, dass Sie Ihre Antwort so bearbeitet haben, dass sie nur unter Windows funktioniert. Ich denke, das ist in Ordnung.
Kraxor

1
@herohuyongtao OP erwähnte, dass er ls nicht analysieren kann, was bedeutet, dass er wahrscheinlich unter Unix ist. Trotzdem eine gute Antwort für Windows.
Thomas

2
Am Ende habe ich ein std::vector<std::wstring>und dann fileName.c_str()anstelle eines Vektors von Zeichenfolgen verwendet, die nicht kompiliert werden konnten.
PerryC

51

Überprüfen Sie dies für eine Nur-C-Lösung. Es ist nur ein zusätzlicher Header erforderlich:

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

Einige Vorteile gegenüber anderen Optionen:

  • Es ist portabel - umschließt POSIX dirent und Windows FindFirstFile
  • Es wird verwendet, readdir_rwo verfügbar, was bedeutet, dass es (normalerweise) threadsicher ist
  • Unterstützt Windows UTF-16 über dieselben UNICODEMakros
  • Es ist C90, so dass selbst sehr alte Compiler es verwenden können

2
Sehr schöner Vorschlag. Ich habe es noch nicht auf einem Windows-Computer getestet, aber es funktioniert hervorragend unter OS X.
ArtOfWarfare

Die Bibliothek unterstützt std :: string nicht, daher können Sie file.c_str () nicht an tinydir_open übergeben. In diesem Fall wird beim Kompilieren auf msvc 2015 der Fehler C2664 ausgegeben.
Stepan Yakovenko

33

Ich empfehle die Verwendung globmit diesem wiederverwendbaren Wrapper. Es werden vector<string>entsprechende Dateipfade generiert , die zum Glob-Muster passen:

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

Was dann mit einem normalen System-Platzhaltermuster aufgerufen werden kann, wie zum Beispiel:

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

2
Testen Sie, ob glob () Null zurückgibt.
Camille Goudeseune

Ich möchte glob.h wie von Ihnen empfohlen verwenden. Trotzdem kann ich die .h-Datei nicht einschließen: Es heißt No such file or directory. Können Sie mir bitte sagen, wie ich dieses Problem lösen kann?
Tofuw

Beachten Sie, dass diese Routine nur eine Ebene tief ist (keine Rekursion). Es wird auch nicht schnell überprüft, ob es sich um eine Datei oder ein Verzeichnis handelt. Dies können Sie einfach tun, indem Sie GLOB_TILDEmit GLOB_TILDE | GLOB_MARKPfaden wechseln und dann nach Pfaden suchen, die mit einem Schrägstrich enden. Sie müssen beide Änderungen vornehmen, wenn Sie dies benötigen.
Volomike

Ist das plattformübergreifend kompatibel?
Nikhil Augustine

Leider können Sie keine einheitlich versteckten Dateien über finden glob.
LmTinyToon

23

Hier ist ein sehr einfacher Code für die C++11Verwendung der boost::filesystemBibliothek zum Abrufen von Dateinamen in einem Verzeichnis (ohne Ordnernamen):

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

Die Ausgabe ist wie folgt:

file1.txt
file2.dat

Hallo, und wo kann ich diese Bibliothek bekommen?
Alexander Leon VI

2
@Alexander De Leon: Sie können diese Bibliothek auf ihrer Website boost.org herunterladen . Lesen Sie zuerst den Leitfaden für die ersten Schritte und verwenden Sie dann die boost::filesystemBibliothek boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Bad

23

Warum nicht verwenden 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;
}

Dies könnte eine bessere Antwort sein, wenn Sie die erforderlichen Includes erläutern.
Volomike

2
Testen Sie, dass glob () Null zurückgibt!
Orbitcowboy

Dies ist gut, wenn Sie die Datei kennen, nach der Sie suchen, wie * .txt
Kemin Zhou

20

Ich denke, das folgende Snippet kann verwendet werden, um alle Dateien aufzulisten.

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

Es folgt die Struktur des Strukturverzeichnisses

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

Ich würde diesen mögen.
Selbststart

10

Versuchen Sie Boost für die X-Plattform-Methode

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

oder verwenden Sie einfach Ihr betriebssystemspezifisches Dateimaterial.


Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert. - Von der Überprüfung
ice1000

@ ice1000 Ernsthaft? Diese Q & A ist von 2009
Tim

8

Schauen Sie sich diese Klasse an, die die Win32-API verwendet. Erstellen foldernameSie einfach eine Instanz, indem Sie die gewünschte Instanz angeben, und rufen Sie dann die getNextFileMethode auf, um die nächste filenameaus dem Verzeichnis abzurufen. Ich denke es braucht windows.hund 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 Handbuch FTW

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

Manchmal ist es auch gut, direkt zur Quelle zu gehen (Wortspiel beabsichtigt). Sie können viel lernen, indem Sie sich die Innereien einiger der häufigsten Befehle unter Linux ansehen. Ich habe einen einfachen Spiegel der GNU-Coreutils auf Github eingerichtet (zum Lesen).

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

Möglicherweise wird Windows nicht angesprochen, aber mit diesen Methoden können einige Fälle der Verwendung von Unix-Varianten auftreten.

Ich hoffe, das hilft...


5

Shreevardhan Antwort funktioniert großartig. Wenn Sie es jedoch in C ++ 14 verwenden möchten, nehmen Sie einfach eine Änderung vornamespace fs = experimental::filesystem;

dh

#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

Ich hoffe dieser Code hilft Ihnen.

#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_tGibt die Adresse einer lokalen Variablen zurück. Außerdem sollten Sie wahrscheinlich die in WinAPI verfügbaren Konvertierungsmethoden verwenden, anstatt Ihre eigenen zu schreiben.
Daniel Kamil Kozar

3

Diese Implementierung verwirklicht Ihren Zweck und füllt dynamisch ein Array von Zeichenfolgen mit dem Inhalt des angegebenen Verzeichnisses.

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

Wie würde ich das nennen? Ich erhalte Segfaults, wenn ich versuche, diese Funktion im ersten ifBlock auszuführen . Ich rufe es mitchar **list; int numItems; exploreDirectory("/folder",list, numItems);
Hal T

2

Das funktioniert bei mir. Es tut mir leid, wenn ich mich nicht an die Quelle erinnern kann. Es ist wahrscheinlich von einer Manpage.

#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

Mit std :: experimental :: filesystem :: directory_iterator () können Sie alle Dateien direkt in Ihrem Stammverzeichnis abrufen. Lesen Sie dann den Namen dieser Pfaddateien.

#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

Diese Antwort sollte für Windows-Benutzer funktionieren, bei denen Probleme mit Visual Studio mit einer der anderen Antworten aufgetreten sind.

  1. Laden Sie die Datei dirent.h von der Github-Seite herunter. Es ist jedoch besser, einfach die Datei Raw dirent.h zu verwenden und die folgenden Schritte auszuführen (so habe ich sie zum Laufen gebracht).

    Github-Seite für dirent.h für Windows: Github-Seite für dirent.h

    Raw Dirent-Datei: Raw dirent.h-Datei

  2. Gehen Sie zu Ihrem Projekt und fügen Sie ein neues Element hinzu ( Ctrl+ Shift+ A). Fügen Sie eine Header-Datei (.h) hinzu und nennen Sie sie dirent.h.

  3. Fügen Sie den Raw dirent.h Datei - Code in der Kopfzeile.

  4. Fügen Sie "dirent.h" in Ihren Code ein.

  5. Fügen Sie die folgende void filefinder()Methode in Ihren Code ein und rufen Sie sie von Ihrer mainFunktion aus auf oder bearbeiten Sie die Funktion so, wie Sie sie verwenden möchten.

    #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 nennen es!

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

Dann lesen Sie einfach die Datei.

BEARBEITEN: Diese Antwort sollte als Hack betrachtet werden, aber sie funktioniert wirklich (wenn auch plattformspezifisch), wenn Sie keinen Zugang zu eleganteren Lösungen haben.


7
Ich darf den Befehl 'ls' nicht ausführen und die Ergebnisse in meinem Programm analysieren. Ich wusste, dass es jemanden geben würde, der so etwas senden würde ...
yyny

1

Da Dateien und Unterverzeichnisse eines Verzeichnisses im Allgemeinen in einer Baumstruktur gespeichert sind, besteht eine intuitive Möglichkeit darin, den DFS-Algorithmus zu verwenden, um jedes von ihnen rekursiv zu durchlaufen. Hier ist ein Beispiel für ein Windows-Betriebssystem unter Verwendung der grundlegenden Dateifunktionen in io.h. Sie können diese Funktionen auf einer anderen Plattform ersetzen. Was ich ausdrücken möchte, ist, dass die Grundidee von DFS dieses Problem perfekt erfüllt.

#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

Ich habe versucht, dem Beispiel in beiden Antworten zu folgen, und es könnte erwähnenswert sein, dass es so aussieht, als ob std::filesystem::directory_entryes geändert wurde, um keine Überlastung des <<Operators zu haben. Stattdessen musste std::cout << p << std::endl;ich Folgendes verwenden, um es kompilieren und zum Laufen bringen zu können:

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

Der Versuch, sich selbst weiterzugeben p, std::cout <<führte zu einem fehlenden Überlastungsfehler.


1

Aufbauend auf dem, was herohuyongtao gepostet hat, und einigen anderen Posts:

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

Was ist der erwartete Eingabetyp von FindFirstFile?

Wie konvertiere ich wstring in string?

Dies ist eine Windows-Lösung.

Da ich std :: string übergeben und einen Vektor von Strings zurückgeben wollte, musste ich einige Konvertierungen vornehmen.

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

Wenn Sie wissen, dass Sie nur Multibyte verwenden WIN32_FIND_DATAA, können Sie FindFirstFileAund verwenden FindNextFileA. Dann müssen Sie das Ergebnis nicht in Multibyte oder die Eingabe in Unicode konvertieren.
Kiran Thilak

0

Nur etwas, das ich teilen möchte und danke für das Lesematerial. Spielen Sie ein bisschen mit der Funktion herum, um sie zu verstehen. Es könnte dir gefallen. e stand für Erweiterung, p steht für Pfad und s steht für Pfadtrennzeichen.

Wenn der Pfad ohne Ende des Trennzeichens übergeben wird, wird ein Trennzeichen an den Pfad angehängt. Wenn für die Erweiterung eine leere Zeichenfolge eingegeben wird, gibt die Funktion alle Dateien zurück, deren Name keine Erweiterung enthält. Wenn ein einzelner Stern eingegeben wurde, werden alle Dateien im Verzeichnis zurückgegeben. Wenn die Länge e größer als 0 ist, aber kein einzelnes * ist, wird e ein Punkt vorangestellt, wenn e keinen Punkt an der Nullposition enthalten hat.

Für einen Rückgabewert. Wenn eine Karte mit der Länge Null zurückgegeben wird, wurde nichts gefunden, aber das Verzeichnis war in Ordnung geöffnet. Wenn der Index 999 über den Rückgabewert verfügbar ist, die Kartengröße jedoch nur 1 beträgt, bedeutet dies, dass beim Öffnen des Verzeichnispfads ein Problem aufgetreten ist.

Beachten Sie, dass diese Funktion aus Effizienzgründen in drei kleinere Funktionen unterteilt werden kann. Darüber hinaus können Sie eine Aufruferfunktion erstellen, die anhand der Eingabe erkennt, welche Funktion aufgerufen werden soll. Warum ist das effizienter? Wenn Sie alles abrufen möchten, was eine Datei ist, wird bei dieser Methode die Unterfunktion, die zum Abrufen aller Dateien erstellt wurde, nur alle Dateien abrufen und muss nicht jedes Mal, wenn eine Datei gefunden wird, eine andere unnötige Bedingung auswerten.

Dies gilt auch, wenn Sie Dateien abrufen, die keine Erweiterung haben. Eine bestimmte zu diesem Zweck erstellte Funktion würde nur dann das Wetter bewerten, wenn es sich bei dem gefundenen Objekt um eine Datei handelt und ob der Name der Datei einen Punkt enthält oder nicht.

Das Speichern ist möglicherweise nicht viel, wenn Sie nur Verzeichnisse mit weniger Dateien lesen. Wenn Sie jedoch eine große Menge an Verzeichnissen lesen oder wenn das Verzeichnis einige hunderttausend Dateien enthält, kann dies eine enorme Einsparung bedeuten.

#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

Das hat bei mir funktioniert. Es schreibt eine Datei mit nur den Namen (kein Pfad) aller Dateien. Dann liest es diese txt-Datei und druckt sie für Sie aus.

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

    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.