Comment puis-je obtenir la liste des fichiers dans un répertoire en utilisant C ou c++?
24 réponses
Dans les tâches petites et simples, je n'utilise pas 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;
}
C'est juste un petit fichier d'en-tête et fait la plupart des choses simples dont vous avez besoin sans utiliser une grande approche basée sur un modèle comme boost (pas d'infraction, j'aime boost!).
L'auteur de la couche de compatibilité windows est Toni Ronkko. Dans Unix, c'est un en-tête standard.
Mise à jour 2017:
En C++17, Il existe maintenant un moyen officiel de lister les fichiers de votre système de fichiers: std::filesystem
. Il y a 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 (auto & p : fs::directory_iterator(path))
std::cout << p << std::endl;
}
Considérez upvoting sa réponse, si vous utilisez L'approche C++17.
Malheureusement, le standard C++ ne définit pas une manière standard de travailler avec des fichiers et des dossiers de cette manière.
Comme il n'y a pas de méthode multiplateforme, la meilleure méthode multiplateforme est d'utiliser une bibliothèque telle que le module boost filesystem.
Méthode de boost multiplateforme:
La fonction suivante, avec un chemin de répertoire et un nom de fichier, Recherche récursivement le répertoire et ses sous-répertoires pour le nom de fichier, renvoyant un bool, et en cas de succès, le chemin d'accès au fichier qui a été 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 boost mentionnée ci-dessus.
Pour les systèmes basés sur Unix/Linux:
Vous pouvez utiliser fonctions opendir / readdir / closedir.
Exemple de code qui recherche un répertoire pour l'entrée " nom " 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 basé sur windows:
Vous pouvez utiliser l'API Win32 , FindFirstFile / FindNextFile / FindClose fonctions.
L'exemple C++ suivant vous 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.
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 & p : fs::directory_iterator(path))
std::cout << p << std::endl; // "p" is the directory entry. Get the path with "p.path()".
}
Aussi, std::filesystem::recursive_directory_iterator
peut itérer les sous-répertoires aussi bien.
Une 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 C Uniquement, veuillez vérifier ceci. Il ne nécessite qu'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:
- c'est portable - wraps POSIX dirent et Windows FindFirstFile
- Il utilise
readdir_r
lorsque disponible, ce qui signifie qu'il est (généralement) threadsafe - prend en charge Windows UTF-16 via les mêmes macros
UNICODE
- C'est C90 donc même les compilateurs très anciens peuvent l'utiliser
Je recommande d'utiliser glob
avec ce wrapper 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;
}
Qui peut alors être appelé avec un modèle générique système normal tel que:
vector<string> files = globVector("./*");
Voici un code très simple dans C++11
en utilisant la bibliothèque boost::filesystem
pour obtenir les noms de fichiers dans un répertoire (à l'exclusion 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, ci-dessous extrait 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 dirent
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 au système D'exploitation.
Découvrez cette classe qui utilise l'api win32. Il suffit de construire une instance en fournissant le foldername
à partir duquel vous voulez la liste, puis appelez la méthode getNextFile
pour obtenir le prochain 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 apprendre beaucoup en regardant 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
Peut-être cela ne s'adresse pas à Windows, mais un certain nombre de cas D'utilisation de variantes Unix peuvent être rencontrés en utilisant ces méthodes.
J'espère que ça aide...
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;
}
Cette implémentation réalise votre objectif en remplissant dynamiquement 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;
}
Cela fonctionne pour moi. Je suis désolé si Je ne me souviens pas de la source. C'est 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 en C++14, faites simplement un changement 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;
}
Vous pouvez obtenir tous les fichiers directs dans votre répertoire racine en utilisant std:: experimental::filesystem:: directory_iterator(). Ensuite, lisez le nom de ces fichiers de chemin.
#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;
}
Système appelez-le!
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 d'une manière spécifique à la plate-forme) si vous n'avez pas accès à des solutions plus élégantes.
Cette réponse devrait fonctionner pour les utilisateurs de Windows qui ont eu du mal à le faire fonctionner avec Visual Studio avec l'une des autres réponses.
-
Téléchargez le dirent.h fichier de la page github. Mais il vaut mieux simplement utiliser le dirent brut.h fichier et suivez mes étapes ci-dessous (c'est comme ça que je l'ai fait fonctionner).
Page Github pour dirent.h Pour Windows: page Github pour dirent.h
Fichier Dirent brut: dirent brut.H Fichier
-
Allez à votre projet et Ajouter un nouvel Élément (Ctrl+Maj+Un). Ajouter un en-tête de fichier (.h) et nommez-le dirent.H.
Collez le Cru dirent.h Fichier code dans votre en-tête.
Inclure "dirent.h" dans votre code.
-
Mettez la méthode
void filefinder()
ci-dessous dans votre code et appelez-la à partir de votre Fonctionmain
ou modifiez la fonction comme vous voulez 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); }
Étant donné que les fichiers et sous-répertoires d'un répertoire sont généralement stockés dans une arborescence, une manière intuitive est d'utiliser l'algorithme DFS pour parcourir récursivement chacun d'eux. Voici un exemple dans le système d'exploitation windows en utilisant les fonctions de fichier de base dans io.h. Vous pouvez remplacer ces fonctions dans 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);
}
J'ai essayé de suivre l'exemple donné dans les deux réponses et il est intéressant de noter qu'il semble que std::filesystem::directory_entry
a été modifié pour ne pas avoir une surcharge de la <<
opérateur. Au lieu de std::cout << p << std::endl;
, j'ai dû utiliser ce qui suit 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.
Juste quelque chose que je veux partager et vous remercier pour le matériel de lecture. Jouez un peu avec la fonction pour la comprendre. Vous pouvez, comme elle. e signifie extension, p est pour chemin, et s est pour séparateur de chemin.
Si le chemin est passé sans fin separator, un séparateur sera ajouté au chemin. Pour l'extension, si une chaîne vide est entrée, la fonction retournera tout fichier qui n'a 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 est de longueur supérieure à 0 mais n'est pas un seul*, un point sera ajouté à e si e n'avaient pas présenté d'un 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 correctement. Si l'index 999 est disponible à partir de la valeur de retour mais que la taille de la carte est seulement 1, cela signifiait qu'il y avait un problème avec l'ouverture du chemin du répertoire.
Notez que pour plus d'efficacité, cette fonction peut être divisé en 3 plus petites fonctions. En plus de cela, vous pouvez créer une fonction d'appelant qui détectera la fonction qu'il va appeler en fonction de l'entrée. Pourquoi est-ce plus efficace? Dit que si vous allez saisir tout ce qui est un fichier, faire cette méthode la sous-fonction qui a construit pour saisir tous les fichiers va juste saisir tout ce qui sont des fichiers et n'a pas besoin d'évaluer toute autre condition inutile chaque fois qu'il a trouvé un fichier.
Cela s'appliquerait également lorsque vous prenez les fichiers qui n'ont pas d'extension. Une fonction construite spécifique à cet effet n'évaluerait que la météo si l'objet trouvé est un fichier et si oui ou non si le nom du fichier contient un point.
L'économie peut ne pas être beaucoup si vous ne lisez que des répertoires avec pas beaucoup de fichiers. Mais si vous lisez une quantité massive de répertoire ou si le répertoire contient quelques centaines de milliers de fichiers, cela pourrait être une économie énorme.
#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;
}
Cela a fonctionné pour moi. Il écrit un fichier avec juste les noms (pas de sentier) de tous les fichiers. Ensuite, il lit ce fichier txt et l'imprime pour vous.
void DisplayFolderContent()
{
system("dir /n /b * > file_names.txt");
char ch;
std::fstream myStream("file_names.txt", std::fstream::in);
while (myStream.get(ch))
{
std::cout << ch;
}
}