Frage

Wie iterieren Sie durch jede Datei / Verzeichnis rekursiv in Standard C ++?

War es hilfreich?

Lösung

In Standard-C ++, technisch gibt es keine Möglichkeit, dies da Standard-C ++ zu tun hat keine Vorstellung von Verzeichnissen. Wenn Sie Ihr Netz ein wenig erweitern möchten, können Sie bei Verwendung von Boost.Filesystem . Dies hat für die Aufnahme in TR2 angenommen worden, so das gibt Ihnen die beste Chance, Ihre Implementierung so nah wie möglich an den Standard zu halten.

Ein Beispiel, genommen direkt von der Website:

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

Andere Tipps

Wenn der Win32-API verwenden, können Sie verwenden, um den Findfirstfile und Findnextfile Funktionen.

http://msdn.microsoft.com/en -US / library / aa365200 (VS.85) aspx

Für rekursive Traversal von Verzeichnissen müssen Sie jedes WIN32_FIND_DATA.dwFileAttributes prüfen zu überprüfen, ob die FILE_ATTRIBUTE_DIRECTORY gesetzt ist. Wenn das Bit gesetzt ist, dann können Sie rekursiv die Funktion mit diesem Verzeichnis aufrufen. Alternativ können Sie einen Stapel für verwenden, um die gleiche Wirkung eines rekursiven Aufruf bereitstellt, aber für sehr langen Pfad Bäume Stack-Überlauf zu vermeiden.

#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        } 

        do {
            if (wcscmp(ffd.cFileName, L".") != 0 && 
                wcscmp(ffd.cFileName, L"..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.push(path + L"\\" + ffd.cFileName);
                }
                else {
                    files.push_back(path + L"\\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}

int main(int argc, char* argv[])
{
    vector<wstring> files;

    if (ListFiles(L"F:\\cvsrepos", L"*", files)) {
        for (vector<wstring>::iterator it = files.begin(); 
             it != files.end(); 
             ++it) {
            wcout << it->c_str() << endl;
        }
    }
    return 0;
}

Sie können es machen, auch mit dem neuen C ++ 11 Bereich basierte for und Erhöhung :

#include <boost/filesystem.hpp>

using namespace boost::filesystem;    
struct recursive_directory_range
{
    typedef recursive_directory_iterator iterator;
    recursive_directory_range(path p) : p_(p) {}

    iterator begin() { return recursive_directory_iterator(p_); }
    iterator end() { return recursive_directory_iterator(); }

    path p_;
};

for (auto it : recursive_directory_range(dir_path))
{
    std::cout << it << std::endl;
}

In C ++ 11/14 mit dem "Filesystem TS", die <experimental/filesystem> Kopf- und Entfernungs-for Sie können dies einfach tun:

#include <experimental/filesystem>

using std::experimental::filesystem::recursive_directory_iterator;
...
for (auto& dirEntry : recursive_directory_iterator(myPath))
     cout << dirEntry << endl;

Wie von C ++ 17, ist std::filesystem Teil der Standardbibliothek und kann im <filesystem>-Header (nicht mehr „experimentell“) gefunden werden.

Eine schnelle Lösung wird unter Verwendung von C dirent.h Bibliothek.

Arbeitscodefragment aus Wikipedia:

#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}

Zusätzlich zu den oben genannten boost :: filesystem WxWidgets :: wxDir und Qt :: QDir .

Sowohl wxWidgets und Qt sind Open Source, Cross-Plattform-C ++ Frameworks.

wxDir bietet eine flexible Möglichkeit, um Dateien zu durchqueren rekursiv Traverse() oder eine einfachere GetAllFiles() Funktion. Wie gut können Sie die Traversal mit GetFirst() und GetNext() Funktionen implementieren (Ich gehe davon aus, dass Traverse () und GetAllFiles () ist Wrapper, die schließlich GetFirst () und GetNext () Funktionen).

QDir ermöglicht den Zugriff auf Verzeichnisstrukturen und deren Inhalte. Es gibt mehrere Möglichkeiten, Verzeichnisse mit QDir zu durchqueren. Sie können über den Verzeichnisinhalt (einschließlich Unterverzeichnisse) mit QDirIterator iterieren, die mit QDirIterator :: Subdirectories Flag instanziiert wurde. Ein anderer Weg ist QDir der GetEntryList () Funktion und Implementierung eines rekursiven Traversal zu verwenden.

Hier ist Beispielcode (entnommen aus hier # Beispiel 8-5) das zeigt, wie alle Unterverzeichnisse iterieren.

#include <qapplication.h>
#include <qdir.h>
#include <iostream>

int main( int argc, char **argv )
{
    QApplication a( argc, argv );
    QDir currentDir = QDir::current();

    currentDir.setFilter( QDir::Dirs );
    QStringList entries = currentDir.entryList();
    for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry) 
    {
         std::cout << *entry << std::endl;
    }
    return 0;
}

boost :: filesystem bietet recursive_directory_iterator, die für diese Aufgabe ist sehr bequem:

#include "boost/filesystem.hpp"
#include <iostream>

using namespace boost::filesystem;

recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
    std::cout << *it << std::endl;                                    
}

Sie können mit ftw(3) oder nftw(3) einer Dateisystem-Hierarchie in C oder C ++ auf gehen POSIX Systeme.

Sie dies nicht tun. Die C ++ Standard hat kein Konzept für Verzeichnisse. Es liegt an die Umsetzung eine Zeichenfolge in eine Datei-Handle zu drehen. Der Inhalt dieser Zeichenfolge und was es abbildet, ist abhängig vom Betriebssystem. Beachten Sie, dass C ++ verwendet werden kann, dass die OS zu schreiben, so wird sie in einer Menge verwendet, wo, wie gefragt ein Verzeichnis zu durchlaufen ist noch nicht definiert (weil Sie den Verzeichnis-Management-Code schreiben).

Sehen Sie sich Ihre OS API-Dokumentation, wie dies zu tun. Wenn Sie tragbar sein müssen, werden Sie eine Reihe von #ifdef s haben müssen für verschiedene Betriebssysteme.

Sie würden wahrscheinlich am besten entweder mit boost oder c ++ 14 experimentellen Dateisystem Sachen. IF Sie sind ein internes Verzeichnis Parsing (dh. Für Ihr Programm verwendet, um Daten zu speichern, nachdem das Programm geschlossen wurde), dann eine Indexdatei machen, die einen Index der Dateiinhalte haben. By the way, müssten Sie wahrscheinlich Auftrieb in Zukunft verwenden, so dass, wenn Sie es nicht installiert haben, installieren Sie! Zweitens, können Sie eine bedingte Kompilierung verwenden, z.

#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif

Der Code für jeden Fall wird genommen von https://stackoverflow.com/a/67336/7077165

#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        } 

        do {
            if (wcscmp(ffd.cFileName, L".") != 0 && 
                wcscmp(ffd.cFileName, L"..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.push(path + L"\\" + ffd.cFileName);
                }
                else {
                    files.push_back(path + L"\\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}
#endif
//so on and so forth.

Sie müssen OS-spezifische Funktionen für Dateisystem-Traversal nennen, wie open() und readdir(). Der C-Standard legt keine Dateisystem-bezogene Funktionen.

Sie dies nicht tun. Standard C ++ aussetzt nicht Konzept eines Verzeichnisses. Insbesondere gibt es nicht irgendeine Möglichkeit, alle Dateien in einem Verzeichnis aufzulisten.

Ein schrecklicher Hack wäre System zu verwenden () Anrufe und die Ergebnisse zu analysieren. Die vernünftigste Lösung wäre eine Art von Cross-Plattform-Bibliothek zu verwenden, wie zum Beispiel Qt oder sogar POSIX .

Wir sind im Jahr 2019. Wir haben Dateisystem Standard-Bibliothek in C++. Die Filesystem library bietet die Möglichkeit für Operationen auf Dateisysteme und deren Komponenten, wie zum Beispiel Pfade, reguläre Dateien und Verzeichnisse durchführen.

Es ist ein wichtiger Hinweis auf diesem Link wenn Sie Portabilität Probleme in Betracht ziehen. Dort heißt es:

  

Die Dateisystem-Bibliothek Einrichtungen möglicherweise nicht verfügbar sein, wenn ein hierarchisches Dateisystem nicht zugänglich für die Umsetzung ist, oder wenn es nicht die erforderlichen Fähigkeiten bietet. Einige Funktionen sind möglicherweise nicht verfügbar, wenn sie nicht durch das zugrunde liegende Dateisystem unterstützt (zum Beispiel des FAT-Dateisystem fehlt symbolische Links und verbietet mehrere Hardlinks). In diesen Fällen müssen Fehler gemeldet werden.

2015 und fusionierte schließlich nach ISO C ++ als C ++ 17:

Die Dateisystem-Bibliothek wurde ursprünglich als boost.filesystem entwickelt, wurde als technische Spezifikation ISO / IEC TS 18822 veröffentlicht. Die Umsetzung boost ist derzeit auf mehr Compiler und Plattformen als die C ++ 17-Bibliothek.

@ adi-Shavit hat diese Frage beantwortet, wenn es Teil von std :: ist experimentell und er diese Antwort im Jahr 2017 aktualisiert wird ich mehr Details über die Bibliothek geben mag und zeigen ausführlicheres Beispiel.

std :: filesystem :: recursive_directory_iterator ist ein LegacyInputIterator, die über iteriert die directory_entry Elemente eines Verzeichnisses und rekursiv über die Einträge aller Unterverzeichnisse. Die Iterationsreihenfolge ist nicht spezifiziert, mit der Ausnahme, dass jeder Verzeichniseintrag nur einmal besucht wird.

Wenn Sie nicht wollen, rekursiv über die Einträge der Unterverzeichnisse iterieren, dann directory_iterator verwendet werden.

Die beiden Iteratoren gibt ein Objekt von directory_entry . directory_entry hat verschiedene nützliche Member-Funktionen wie is_regular_file, is_directory, is_socket, is_symlink usw. Die path() Member-Funktion gibt ein Objekt von std :: filesystem :: path und es kann verwendet werden file extension, filename, root name zu erhalten.

Betrachten Sie das folgende Beispiel. Ich habe Ubuntu benutze und sie über das Terminal zusammengestellt mit

g ++ example.cpp --std = C ++ 17 ++ -lstdc fs -Wall

#include <iostream>
#include <string>
#include <filesystem>

void listFiles(std::string path)
{
    for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
        if (!dirEntry.is_regular_file()) {
            std::cout << "Directory: " << dirEntry.path() << std::endl;
            continue;
        }
        std::filesystem::path file = dirEntry.path();
        std::cout << "Filename: " << file.filename() << " extension: " << file.extension() << std::endl;

    }
}

int main()
{
    listFiles("./");
    return 0;
}

Wenn Sie auf Windows verfügbar sind, können Sie die Findfirstfile verwenden zusammen mit Findnextfile-API. Sie können FindFileData.dwFileAttributes verwenden um zu überprüfen, ob ein gegebener Pfad eine Datei oder ein Verzeichnis ist. Wenn es ein Verzeichnis ist, können Sie rekursiv den Algorithmus wiederholen.

Hier habe ich einige Code zusammengestellt, die alle Dateien auf einem Windows-Rechner auflistet.

http://dreams-soft.com/projects/traverse-directory

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top