Wie iteriert man in Standard-C ++ rekursiv durch jede Datei / jedes Verzeichnis?
Wie iteriert man in Standard-C ++ rekursiv durch jede Datei / jedes Verzeichnis?
Antworten:
In Standard-C ++ gibt es technisch keine Möglichkeit, dies zu tun, da Standard-C ++ keine Konzeption von Verzeichnissen hat. Wenn Sie Ihr Netz ein wenig erweitern möchten, sollten Sie sich Boost.FileSystem ansehen . 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 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;
}
Ab C ++ 17, dem <filesystem>Header und dem Bereich forkönnen Sie dies einfach tun:
#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 std::filesystemist Teil der Standardbibliothek und befindet sich im <filesystem>Header (nicht mehr "experimentell").
using, verwenden Sie namespacestattdessen.
Wenn Sie die Win32-API verwenden, können Sie die Funktionen FindFirstFile und FindNextFile verwenden.
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
Für das rekursive Durchlaufen von Verzeichnissen müssen Sie jedes WIN32_FIND_DATA.dwFileAttributes überprüfen, um zu überprüfen, ob das Bit FILE_ATTRIBUTE_DIRECTORY gesetzt ist. Wenn das Bit gesetzt ist, können Sie die Funktion mit diesem Verzeichnis rekursiv aufrufen. Alternativ können Sie einen Stapel verwenden, um den gleichen Effekt eines rekursiven Aufrufs 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;
}
Mit der neuen C ++ 11- Reihe forund Boost 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;
}
Eine schnelle Lösung ist die Verwendung der Dirent.h- Bibliothek von C.
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 dem oben erwähnten Boost :: -Dateisystem möchten Sie möglicherweise wxWidgets :: wxDir und Qt :: QDir untersuchen .
Sowohl wxWidgets als auch Qt sind plattformübergreifende Open Source-C ++ - Frameworks.
wxDirbietet eine flexible Möglichkeit, Dateien rekursiv zu durchlaufen, Traverse()oder eine einfachere GetAllFiles()Funktion. Sie können auch das Traversal mit GetFirst()und GetNext()Funktionen implementieren (ich gehe davon aus, dass Traverse () und GetAllFiles () Wrapper sind, die schließlich die Funktionen GetFirst () und GetNext () verwenden).
QDirbietet Zugriff auf Verzeichnisstrukturen und deren Inhalte. Es gibt verschiedene Möglichkeiten, Verzeichnisse mit QDir zu durchlaufen. Sie können den Verzeichnisinhalt (einschließlich der Unterverzeichnisse) mit QDirIterator durchlaufen, der 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 Beispielcode (aus hier # Beispiel 8-5) , die 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, was 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;
}
Sie können eine Dateisystemhierarchie in C oder C ++ auf POSIX- Systemen verwenden ftw(3)odernftw(3) durchlaufen .
nftw()Verwendung.
Sie sind wahrscheinlich am besten mit Boost oder dem experimentellen Dateisystem von c ++ 14 vertraut. Wenn Sie ein internes Verzeichnis analysieren (dh für Ihr Programm zum Speichern von Daten nach dem Schließen des Programms verwendet werden), erstellen Sie eine Indexdatei mit einem Index des Dateiinhalts. Übrigens müssten Sie wahrscheinlich in Zukunft Boost verwenden. Wenn Sie es also nicht installiert haben, installieren Sie es! Zweitens könnten 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 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.
Das tust du nicht. Der C ++ - Standard hat kein Konzept für Verzeichnisse. Es liegt an der Implementierung, eine Zeichenfolge in ein Dateihandle umzuwandeln. Der Inhalt dieser Zeichenfolge und die Zuordnung ist vom Betriebssystem abhängig. Beachten Sie, 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 Betriebssystem-API. Wenn Sie portabel sein müssen, müssen Sie eine Reihe von #ifdefs für verschiedene Betriebssysteme haben.
Sie müssen betriebssystemspezifische Funktionen für das Durchlaufen des Dateisystems aufrufen, z. B. open()und readdir(). Der C-Standard spezifiziert keine dateisystembezogenen Funktionen.
Wir sind im Jahr 2019. Wir haben Dateisystem Standardbibliothek in C++. Das Filesystem librarybietet Funktionen zum Ausführen von Vorgängen auf Dateisystemen und deren Komponenten, z. B. Pfaden, regulären Dateien und Verzeichnissen.
Dieser Link enthält einen wichtigen Hinweis , wenn Sie Probleme mit der Portabilität in Betracht ziehen. Es sagt:
Die Funktionen der Dateisystembibliothek sind möglicherweise nicht verfügbar, wenn ein hierarchisches Dateisystem für die Implementierung nicht zugänglich ist oder wenn es nicht die erforderlichen Funktionen bietet. Einige Funktionen sind möglicherweise nicht verfügbar, wenn sie vom zugrunde liegenden Dateisystem nicht unterstützt werden (z. B. fehlen dem FAT-Dateisystem symbolische Links und es sind mehrere Hardlinks verboten). In diesen Fällen müssen Fehler gemeldet werden.
Die Dateisystembibliothek wurde ursprünglich als entwickelt boost.filesystem, als technische Spezifikation ISO / IEC TS 18822: 2015 veröffentlicht und schließlich ab C ++ 17 mit 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 :: experimentell war, und er hat diese Antwort 2017 aktualisiert. Ich möchte mehr Details über die Bibliothek geben und ein detaillierteres Beispiel zeigen.
std :: filesystem :: recursive_directory_iterator ist ein LegacyInputIteratorBefehl, der ü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_entryhat verschiedene nützliche Member - Funktionen wie is_regular_file, is_directory, is_socket, is_symlinketc. Die path()Member - Funktion gibt ein Objekt von std :: filesystem :: path und es kann zu bekommen verwendet werden file extension, filename, root name.
Betrachten Sie das folgende Beispiel. Ich habe es verwendet Ubuntuund ü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;
}
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 plattformübergreifenden Bibliothek wie Qt oder sogar POSIX .
Sie können verwenden std::filesystem::recursive_directory_iterator. Beachten Sie jedoch, dass dies auch symbolische (weiche) Links umfasst. Wenn Sie sie vermeiden möchten, können Sie verwenden is_symlink. Anwendungsbeispiel:
size_t directorySize(const std::filesystem::path& directory)
{
size_t size{ 0 };
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
{
if (entry.is_regular_file() && !entry.is_symlink())
{
size += entry.file_size();
}
}
return size;
}
Wenn Sie unter Windows arbeiten, können Sie FindFirstFile zusammen mit der FindNextFile-API verwenden. Mit FindFileData.dwFileAttributes können Sie überprüfen, ob ein bestimmter Pfad eine Datei oder ein Verzeichnis ist. Wenn es sich um ein Verzeichnis handelt, können Sie den Algorithmus rekursiv wiederholen.
Hier habe ich einen Code zusammengestellt, der alle Dateien auf einem Windows-Computer auflistet.
Der Dateibaumspaziergang ftwist eine rekursive Methode, um den gesamten Verzeichnisbaum im Pfad zu umwandeln. Weitere Details finden Sie hier .
HINWEIS: Sie können auch ftsversteckte Dateien wie .oder ..oder überspringen.bashrc
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
printf("0%3o\t%s\n", status->st_mode&0777, name);
}
if (type == FTW_D && strcmp(".", name) != 0)
{
printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
Die Ausgabe sieht folgendermaßen aus:
0755 ./Shivaji/
0644 ./Shivaji/20200516_204454.png
0644 ./Shivaji/20200527_160408.png
0644 ./Shivaji/20200527_160352.png
0644 ./Shivaji/20200520_174754.png
0644 ./Shivaji/20200520_180103.png
0755 ./Saif/
0644 ./Saif/Snapchat-1751229005.jpg
0644 ./Saif/Snapchat-1356123194.jpg
0644 ./Saif/Snapchat-613911286.jpg
0644 ./Saif/Snapchat-107742096.jpg
0755 ./Milind/
0644 ./Milind/IMG_1828.JPG
0644 ./Milind/IMG_1839.JPG
0644 ./Milind/IMG_1825.JPG
0644 ./Milind/IMG_1831.JPG
0644 ./Milind/IMG_1840.JPG
Angenommen, Sie möchten einen Dateinamen (Beispiel: Suche nach allen *.jpg, *.jpeg, *.pngDateien) für bestimmte Anforderungen verwenden fnmatch.
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <fnmatch.h>
static const char *filters[] = {
"*.jpg", "*.jpeg", "*.png"
};
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
int i;
for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
/* if the filename matches the filter, */
if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
printf("0%3o\t%s\n", status->st_mode&0777, name);
break;
}
}
}
if (type == FTW_D && strcmp(".", name) != 0)
{
//printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}