Comment puis-je déterminer la liste des fichiers d'un répertoire à partir de mon code C ou C++?
Je ne suis pas autorisé à exécuter la commande ls
et à analyser les résultats à partir de mon programme.
Dans les tâches simples et petites, je n'utilise pas de boost, j'utilise dirent.h, qui est également disponible pour Windows:
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;
}
Il ne s'agit que d'un petit fichier d'en-tête. Il fait la plupart des choses simples dont vous avez besoin sans utiliser une approche basée sur des modèles, telle que boost (aucune infraction, j'aime boost!).
L'auteur de la couche de compatibilité Windows est Toni Ronkko. Sous Unix, c'est un en-tête standard.
UPDATE 2017:
En C++ 17, il existe maintenant un moyen officiel de lister les fichiers de votre système de fichiers: std::filesystem
. Il existe une excellente réponse de Shreevardhan ci-dessous avec ce code source:
#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;
}
C++ 17 a maintenant un std::filesystem::directory_iterator
, qui peut être utilisé comme
#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;
}
De même, std::filesystem::recursive_directory_iterator
peut également parcourir les sous-répertoires.
Malheureusement, la norme C++ ne définit pas de manière standard l'utilisation de fichiers et de dossiers de cette manière.
Comme il n’existe pas de méthode multiplate-forme, la meilleure solution consiste à utiliser une bibliothèque telle que le module de fichiers boost .
Méthode de renforcement de plateforme croisée:
La fonction suivante, à partir d'un chemin de répertoire et d'un nom de fichier, recherche récursivement le nom du fichier dans le répertoire et ses sous-répertoires, en renvoyant un booléen et, en cas de succès, le chemin du fichier trouvé.
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;
}
Source de la page de rappel mentionnée ci-dessus.
Pour les systèmes Unix/Linux:
Vous pouvez utiliser opendir / readdir / closedir .
Un exemple de code recherchant l’entrée «name» dans un répertoire est:
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;
Code source des pages de manuel ci-dessus.
Pour un système Windows:
vous pouvez utiliser l'API Win32 FindFirstFile / FindNextFile / FindClose functions.
L'exemple C++ suivant montre une utilisation minimale de 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);
}
}
Code source des pages msdn ci-dessus.
Une seule fonction suffit, vous n'avez pas besoin d'utiliser une bibliothèque tierce (pour Windows).
#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: comme mentionné par @Sebastian, vous pouvez changer *.*
en *.ext
afin d’obtenir uniquement les fichiers EXT (c’est-à-dire d’un type spécifique) dans ce répertoire.
Pour une solution en C uniquement, veuillez vérifier ceci. Cela nécessite seulement un en-tête supplémentaire:
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);
Quelques avantages par rapport aux autres options:
readdir_r
lorsqu'il est disponible, ce qui signifie qu'il est (généralement) threadsafeUNICODE
Je recommande d'utiliser glob
avec cet emballage réutilisable. Il génère un vector<string>
correspondant aux chemins de fichier qui correspondent au modèle glob:
#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;
}
Ce qui peut ensuite être appelé avec un modèle générique de système normal tel que:
vector<string> files = globVector("./*");
Voici un code très simple dans C++11
utilisant la bibliothèque boost::filesystem
pour obtenir les noms de fichiers dans un répertoire (à l'exception des noms de dossiers):
#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;
}
}
La sortie est comme:
file1.txt
file2.dat
Pourquoi ne pas utiliser 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;
}
Je pense que l'extrait ci-dessous peut être utilisé pour lister tous les fichiers.
#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);
}
Voici la structure de la structure dir
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 */
};
Essayez boost pour la méthode x-platform
http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm
ou utilisez simplement vos fichiers spécifiques à votre système d'exploitation.
Découvrez cette classe qui utilise l'API win32. Créez simplement une instance en fournissant la variable foldername
à partir de laquelle vous souhaitez que la liste soit appelée, puis appelez la méthode getNextFile
pour obtenir la prochaine variable filename
du répertoire. Je pense qu'il a besoin de windows.h
et 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;
}
};
Manuel GNU FTW
Aussi, parfois, il est bon d'aller directement à la source (jeu de mots). Vous pouvez en apprendre beaucoup en examinant les entrailles de certaines des commandes les plus courantes sous Linux. J'ai mis en place un simple miroir des coreutils de GNU sur github (pour la lecture).
https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c
Cela ne concerne peut-être pas Windows, mais plusieurs méthodes d’utilisation des variantes Unix peuvent être utilisées.
J'espère que cela pourra aider...
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;
}
J'espère que ce code vous aidera.
#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;
}
vous pouvez obtenir tous les fichiers directement dans votre répertoire racine en utilisant std :: experimental :: filesystem :: directory_iterator (). Ensuite, lisez le nom de ces pathfiles.
#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;
}
Cela fonctionne pour moi. Je suis désolé si je ne me souviens pas de la source. Cela provient probablement d'une page de manuel.
#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/");
}
La réponse de Shreevardhan fonctionne très bien. Mais si vous voulez l’utiliser dans c ++ 14, apportez une modification namespace fs = experimental::filesystem;
c'est à dire.,
#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;
}
Cette implémentation réalise votre objectif en remplissant de manière dynamique un tableau de chaînes avec le contenu du répertoire spécifié.
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;
}
Cette réponse devrait fonctionner pour les utilisateurs Windows qui ont eu du mal à le faire fonctionner avec Visual Studio avec l'une des autres réponses.
Téléchargez le fichier dirent.h à partir de la page github. Mais il est préférable d’utiliser simplement le fichier Raw dirent.h et de suivre les étapes ci-dessous (c’est ainsi que je l’ai fait fonctionner).
Page Github pour dirent.h pour Windows: page Github pour dirent.h
Fichier Dirent Raw: Fichier Dirent.h Raw
Accédez à votre projet et ajoutez un nouvel élément (Ctrl+Shift+A). Ajoutez un fichier d’en-tête (.h) et nommez-le dirent.h.
Collez le code Raw dirent.h File dans votre en-tête.
Incluez "dirent.h" dans votre code.
Placez la méthode void filefinder()
ci-dessous dans votre code et appelez-la à partir de votre fonction main
ou modifiez la fonction de la façon dont vous souhaitez l'utiliser.
#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);
}
Le système l'appelle!
system( "dir /b /s /a-d * > file_names.txt" );
Ensuite, il suffit de lire le fichier.
EDIT: Cette réponse devrait être considérée comme un hack, mais cela fonctionne vraiment (bien que de manière spécifique à une plate-forme) si vous n’avez pas accès à des solutions plus élégantes.
J'ai essayé de suivre l'exemple donné dans les deuxréponses et il convient de noter qu'il apparaît que si std::filesystem::directory_entry
a été modifié pour ne pas surcharger l'opérateur <<
. Au lieu de std::cout << p << std::endl;
, j'ai dû utiliser les éléments suivants pour pouvoir compiler et le faire fonctionner:
#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;
}
essayer de passer p
seul à std::cout <<
a entraîné une erreur de surcharge manquante.
Comme les fichiers et les sous-répertoires d'un répertoire sont généralement stockés dans une arborescence, un moyen intuitif consiste à utiliser l'algorithme DFS pour les parcourir de manière récursive. Voici un exemple dans le système d’exploitation Windows utilisant les fonctions de fichier de base dans io.h. Vous pouvez remplacer ces fonctions sur une autre plate-forme. Ce que je veux exprimer, c'est que l'idée de base de DFS répond parfaitement à ce problème.
#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);
}
Juste quelque chose que je veux partager et merci pour le matériel de lecture. Jouez un peu avec la fonction pour la comprendre. Vous pouvez aimer ça. e correspondait à extension, p à chemin et s à séparateur de chemin.
Si le chemin est passé sans séparateur de fin, un séparateur sera ajouté au chemin. Pour l'extension, si une chaîne vide est entrée, la fonction renverra tout fichier ne portant pas d'extension dans son nom. Si une seule étoile a été entrée, tous les fichiers du répertoire seront retournés. Si e longueur est supérieure à 0 mais n'est pas un simple *, un point sera ajouté à e si e ne contenait pas de point à la position zéro.
Pour une valeur de retour. Si une carte de longueur nulle est retournée, rien n'a été trouvé, mais le répertoire était ouvert, d'accord. Si l'index 999 est disponible à partir de la valeur de retour mais que la taille de la carte n'est que de 1, cela signifiait qu'il y avait un problème avec l'ouverture du chemin du répertoire.
Notez que par souci d'efficacité, cette fonction peut être divisée en 3 fonctions plus petites. En plus de cela, vous pouvez créer une fonction appelant qui détectera la fonction à appeler en fonction de l'entrée. Pourquoi est-ce plus efficace? Si vous voulez récupérer tout ce qui est un fichier, utilisez cette méthode pour que la sous-fonction permettant de récupérer tous les fichiers ne contienne que tous les fichiers et n'a pas besoin d'évaluer toute autre condition inutile chaque fois qu'un fichier est trouvé.
Cela s’appliquerait également lorsque vous récupérerez des fichiers n’ayant pas d’extension. Une fonction spécifique construite à cet effet n'évalue la météo que si l'objet trouvé est un fichier, puis si le nom du fichier contient un point.
L'enregistrement risque de ne pas être important si vous ne lisez que des répertoires contenant moins de fichiers. Mais si vous lisez un grand nombre de répertoires ou si le répertoire contient quelques centaines de milliers de fichiers, cela pourrait représenter une énorme économie.
#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;
}