it-swarm-eu.dev

Wie durchlaufen Sie jede Datei / jedes Verzeichnis in Standard-C ++ rekursiv?

Wie durchlaufen Sie jede Datei/jedes Verzeichnis in Standard-C++ rekursiv?

102
robottobor

In Standard-C++ gibt es technisch keine Möglichkeit, dies zu tun, da Standard-C++ keine Vorstellung von Verzeichnissen hat. Wenn Sie Ihr Netz ein wenig erweitern möchten, schauen Sie sich vielleicht Boost.FileSystem an. Dies wurde für die Aufnahme in TR2 akzeptiert, sodass Sie die beste Chance haben, Ihre Implementierung so nah wie möglich am Standard zu halten.

Ein Beispiel, das direkt von der Website stammt:

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

Bei Verwendung der Win32-API können Sie die Funktionen FindFirstFile und FindNextFile verwenden.

http://msdn.Microsoft.com/en-us/library/aa365200 (VS.85) .aspx

Beim rekursiven Durchlaufen von Verzeichnissen müssen Sie jedes WIN32_FIND_DATA.dwFileAttributes überprüfen, um zu überprüfen, ob das FILE_ATTRIBUTE_DIRECTORY Bit ist gesetzt. Wenn das Bit gesetzt ist, können Sie die Funktion mit diesem Verzeichnis rekursiv aufrufen. Alternativ können Sie einen Stapel verwenden, um denselben Effekt wie bei einem rekursiven Aufruf zu erzielen, aber einen Stapelüberlauf für sehr lange Pfadbäume 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;
}
42
Jorge Ferreira

In C++ 17 wird die <filesystem> header und range -for können Sie einfach folgendermaßen vorgehen:

#include <filesystem>

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

Ab C++ 17 ist std::filesystem ist Teil der Standardbibliothek und befindet sich im <filesystem> Header (nicht mehr "experimentell").

36
Adi Shavit

Mit den neuen bereichsbezogenen for und BoostC++ 11 können Sie es noch einfacher machen:

#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;
}
31
Matthieu G

Eine schnelle Lösung ist die Verwendung der Bibliothek Dirent.h von C.

Arbeitscode-Fragment 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;
}
23
Alex

Zusätzlich zu dem oben erwähnten boost :: filesystem können Sie wxWidgets :: wxDir und Qt :: QDir untersuchen.

Sowohl wxWidgets als auch Qt sind plattformübergreifende Open Source-C++ - Frameworks.

wxDir bietet eine flexible Möglichkeit, Dateien mithilfe von Traverse() oder einer einfacheren GetAllFiles() -Funktion rekursiv zu durchlaufen. Sie können den Traversal auch mit den Funktionen GetFirst() und GetNext() implementieren (ich gehe davon aus, dass Traverse () und GetAllFiles () Wrapper sind, die letztendlich die Funktionen GetFirst () und GetNext () verwenden).

QDir bietet Zugriff auf Verzeichnisstrukturen und deren Inhalt. Es gibt verschiedene Möglichkeiten, mit QDir Verzeichnisse zu durchlaufen. Sie können den Inhalt des Verzeichnisses (einschließlich der Unterverzeichnisse) mit QDirIterator durchlaufen, das mit dem Flag QDirIterator :: Subdirectories instanziiert wurde. Eine andere Möglichkeit besteht darin, die GetEntryList () - Funktion von QDir zu verwenden und eine rekursive Durchquerung zu implementieren.

Hier ist ein Beispielcode (entnommen aus hier # Beispiel 8-5), der zeigt, wie alle Unterverzeichnisse durchlaufen werden.

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

Boost :: filesystem bietet einen rekursiven_Verzeichnis_iterator, der für diese Aufgabe sehr praktisch ist:

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

Sie können ftw(3) oder nftw(3) verwenden, um eine Dateisystemhierarchie in C oder C++ zu durchlaufen. POSIX Systeme.

4
leif

Sie sind wahrscheinlich am besten mit Boost oder dem experimentellen Dateisystem von C++ 14 zurechtgekommen. [~ # ~] wenn Sie [~ # ~] ein internes Verzeichnis analysieren (dh für Ihr Programm verwendet, um Daten zu speichern, nachdem das Programm geschlossen wurde) ), erstellen Sie dann eine Indexdatei, die einen Index des Dateiinhalts enthält. Übrigens müssten Sie Boost wahrscheinlich in Zukunft verwenden. Wenn Sie es also nicht installiert haben, installieren Sie es! 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 stammt aus 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.
4
ndrewxie

Das tust du nicht. Der C++ - Standard kennt keine Verzeichnisse. Es liegt an der Implementierung, einen String in ein Datei-Handle umzuwandeln. Der Inhalt dieser Zeichenfolge und die Zuordnung sind vom Betriebssystem abhängig. Denken Sie daran, dass C++ zum Schreiben dieses Betriebssystems verwendet werden kann, sodass es auf einer Ebene verwendet wird, auf der die Frage, wie ein Verzeichnis durchlaufen werden soll, noch nicht definiert ist (da Sie den Verzeichnisverwaltungscode schreiben).

Informationen dazu finden Sie in der Dokumentation zur OS-API. Wenn Sie portabel sein müssen, müssen Sie eine Reihe von # ifdef s für verschiedene Betriebssysteme haben.

3
Matthew Scouten

Sie müssen betriebssystemspezifische Funktionen für die Dateisystemüberquerung aufrufen, z. B. open() und readdir(). Der C-Standard spezifiziert keine dateisystembezogenen Funktionen.

2
John Millikin

Das tust du nicht. Standard C++ ist nicht dem Konzept eines Verzeichnisses ausgesetzt. Insbesondere gibt es keine Möglichkeit, alle Dateien in einem Verzeichnis aufzulisten.

Ein schrecklicher Hack wäre, system () -Aufrufe zu verwenden und die Ergebnisse zu analysieren. Die vernünftigste Lösung wäre die Verwendung einer Art plattformübergreifender Bibliothek wie Qt oder sogar POSIX .

1
shoosh

Wir sind im Jahr 2019. Wir haben Dateisystem Standardbibliothek in C++. Der Filesystem library Bietet Funktionen zum Ausführen von Vorgängen auf Dateisystemen und deren Komponenten, wie z. B. Pfaden, regulären Dateien und Verzeichnissen.

Es gibt einen wichtigen Hinweis zu dieser Link , wenn Sie Probleme mit der Portabilität haben. Es sagt:

Die Dateisystembibliotheksfunktionen sind möglicherweise nicht verfügbar, wenn die Implementierung nicht auf ein hierarchisches Dateisystem zugreifen kann oder wenn es nicht die erforderlichen Funktionen bietet. Einige Funktionen sind möglicherweise nicht verfügbar, wenn sie nicht vom zugrunde liegenden Dateisystem unterstützt werden (z. B. fehlen dem FAT-Dateisystem symbolische Verknüpfungen und es werden mehrere feste Verknüpfungen verboten). In diesen Fällen müssen Fehler gemeldet werden.

Die Dateisystembibliothek wurde ursprünglich als boost.filesystem Entwickelt, als technische Spezifikation ISO/IEC TS 18822: 2015 veröffentlicht und schließlich ab C++ 17 zu ISO C++ zusammengeführt. Die Boost-Implementierung ist derzeit auf mehr Compilern und Plattformen als in der C++ 17-Bibliothek verfügbar.

@ adi-shavit hat diese Frage beantwortet, als sie Teil von std :: experimental war und er hat diese Antwort 2017 aktualisiert. Ich möchte mehr Details über die Bibliothek geben und ein ausführlicheres Beispiel zeigen.

std :: filesystem :: recursive_directory_iterator ist ein LegacyInputIterator, das über die directory_entry-Elemente eines Verzeichnisses und rekursiv über die Einträge aller Unterverzeichnisse iteriert. Die Iterationsreihenfolge ist nicht angegeben, außer dass jeder Verzeichniseintrag nur einmal besucht wird.

Wenn Sie die Einträge von Unterverzeichnissen nicht rekursiv durchlaufen möchten, sollte directory_iterator verwendet werden.

Beide Iteratoren geben ein Objekt von directory_entry zurück. directory_entry Hat verschiedene nützliche Elementfunktionen wie is_regular_file, is_directory, is_socket, is_symlink Usw. Das path()-Element Die Funktion gibt ein Objekt von std :: filesystem :: path zurück und kann verwendet werden, um file extension, filename, root name zu erhalten.

Betrachten Sie das folgende Beispiel. Ich habe Ubuntu benutzt und über das Terminal mit kompiliert

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