Comment faire pour itérer sur chaque fichier/répertoire de manière récursive en C++ standard ?
Le C++ n'a pas de concept de fichiers ? Et std::fstream ? Ou fopen ?
Comment faire pour itérer sur chaque fichier/répertoire de manière récursive en C++ standard ?
En C++ standard, il n'y a techniquement aucun moyen de le faire puisque le C++ standard n'a aucune conception des répertoires. Si vous voulez élargir un peu votre champ d'action, vous pouvez utiliser la fonction Boost.FileSystem . Il a été accepté pour inclusion dans TR2, ce qui vous donne la meilleure chance de garder votre implémentation aussi proche que possible de la norme.
Un exemple, tiré directement du site web :
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;
}
Mise à jour en fonction de la dernière version de boost : Au cas où quelqu'un tomberait sur cette réponse, la dernière version de boost inclut une classe de commodité boost::recursive_directory_iterator, donc écrire la boucle ci-dessus avec un appel récursif explicite n'est plus nécessaire. Lien : boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/
Vous devriez probablement remplacer itr->leaf() par itr->path().leaf() ('leaf' : n'est pas un membre de 'boost::filesystem::directory_entry')
Si vous utilisez l'API Win32, vous pouvez utiliser la fonction FindFirstFile y FindNextFile fonctions.
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
Pour une traversée récursive des répertoires, vous devez inspecter chaque WIN32_FIND_DATA.dwFileAttributs pour vérifier si le ATTRIBUT_FICHIER_RÉPERTOIRE est activé. Si le bit est activé, vous pouvez appeler récursivement la fonction avec ce répertoire. Vous pouvez également utiliser une pile pour obtenir le même effet qu'un appel récursif mais en évitant le débordement de la pile pour les arbres de chemins très longs.
#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;
}
Combien de temps il t'a fallu pour écrire ça ? Je pense que cela prendrait moins de temps de coller le C++ à Python et de le faire en une seule ligne.
Btw, si quelqu'un veut éditer légèrement le programme pour accepter un paramètre de ligne de commande argv[1] pour le chemin au lieu d'un paramètre codé en dur (" F:\\cvsrepos "), la signature de main(int, char) deviendrait wmain(int, wchar_t) comme ceci : int wmain(int argc, wchar_t *argv[])
Vous pouvez rendre les choses encore plus simples avec la nouvelle fonction C++11 basé sur la gamme for
y Boost :
#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;
}
Une solution rapide consiste à utiliser la fonction Dirent.h bibliothèque.
Fragment de code de travail provenant de 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;
}
En plus de boost::filesystem mentionné ci-dessus, vous voudrez peut-être examiner wxWidgets::wxDir y Qt::QDir .
wxWidgets et Qt sont tous deux des cadres C++ à source ouverte et multiplateformes.
wxDir
fournit un moyen flexible de parcourir les fichiers de manière récursive en utilisant Traverse()
ou un plus simple GetAllFiles()
fonction. De même, vous pouvez implémenter la traversée avec GetFirst()
y GetNext()
(je suppose que Traverse() et GetAllFiles() sont des wrappers qui utilisent éventuellement les fonctions GetFirst() et GetNext()).
QDir
permet d'accéder aux structures de répertoires et à leur contenu. Il existe plusieurs façons de parcourir les répertoires avec QDir. Vous pouvez itérer sur le contenu du répertoire (y compris les sous-répertoires) avec QDirIterator qui a été instancié avec l'indicateur QDirIterator::Subdirectories. Une autre méthode consiste à utiliser la fonction GetEntryList() de QDir et à mettre en œuvre une traversée récursive.
Voici un exemple de code (tiré de aquí # Exemple 8-5) qui montre comment itérer sur tous les sous-répertoires.
#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;
}
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
0 votes
Vous pouvez examiner boost.filesystem boost.org/doc/libs/1_31_0/libs/filesystem/doc/index.htm
2 votes
Pas le C++ standard : pocoproject.org/docs/Poco.DirectoryIterator.html
2 votes
Cela devrait bientôt figurer dans la norme via le Système de fichiers TS avec le Itérateur de répertoire récursif
1 votes
Si l'utilisation d'une bibliothèque C standard n'empêche pas d'appeler un programme C++ comme "standard", nftw() . Voici un exemple pratique exemple
3 votes
Quelqu'un, qui sait ce qu'il fait, devrait prendre une heure pour mettre à jour ceci.
0 votes
Le problème avec toutes les réponses "beauty-shot" basées sur la plage est qu'elles ne sont pas adaptées à une utilisation sérieuse en production, en raison de l'absence de récupération des erreurs par itération. C'est tout ou rien. Le temps que vous attrapiez une exception, la boucle a disparu. Par contre, si vous voulez protéger toutes les parties fragiles individuellement, l'horreur s'ensuit : la boucle doit être disséquée, et le résultat ne ressemblera pas à quoi que ce soit d'un peu joli -- et l'option
++
op (increment()
) pourrait toujours échouent irrémédiablement... C'est à partir de ce moment-là (et des crashs connexes jugés trop coûteux à déboguer) que j'ai abandonné complètement le dir-iterator de la std rec. comme "toujours un jouet".