Wie kann ich die Liste der Dateien in einem Verzeichnis in meinem C- oder C++ - Code ermitteln?
Ich darf den Befehl ls
nicht ausführen und die Ergebnisse aus meinem Programm heraus analysieren.
Bei kleinen und einfachen Aufgaben verwende ich keinen Boost, ich verwende dirent.h, das auch für Windows verfügbar ist:
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;
}
Es ist nur eine kleine Header-Datei und erledigt die meisten einfachen Dinge, die Sie benötigen, ohne einen großen vorlagenbasierten Ansatz wie Boost zu verwenden (kein Angriff, ich mag Boost!).
Der Autor der Windows-Kompatibilitätsebene ist Toni Ronkko. Bei Unix handelt es sich um einen Standardheader.
UPDATE 2017:
In C++ 17 gibt es jetzt eine offizielle Möglichkeit, Dateien Ihres Dateisystems aufzulisten: std::filesystem
. Es gibt eine ausgezeichnete Antwort aus Shreevardhan unten mit diesem Quellcode:
#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 hat jetzt einen std::filesystem::directory_iterator
, der als verwendet werden kann
#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;
}
std::filesystem::recursive_directory_iterator
kann auch die Unterverzeichnisse durchlaufen.
Leider definiert der C++ - Standard keine Standardmethode für das Arbeiten mit Dateien und Ordnern auf diese Weise.
Da es keine plattformübergreifende Methode gibt, ist die beste plattformübergreifende Methode die Verwendung einer Bibliothek wie boost filesystem module .
Plattformübergreifende Boost-Methode:
Wenn die folgende Funktion einen Verzeichnispfad und einen Dateinamen enthält, durchsucht das Verzeichnis und seine Unterverzeichnisse rekursiv nach dem Dateinamen. Dabei wird ein Bool und bei Erfolg der Pfad der gefundenen Datei zurückgegeben.
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;
}
Quelle von der oben genannten Boost-Seite.
Für Unix/Linux-basierte Systeme:
Sie können opendirreaddir / closedir verwenden.
Ein Beispielcode, der ein Verzeichnis nach dem Eintrag "name" durchsucht, lautet:
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;
Quellcode von den obigen Manpages.
_/Für Windows-basierte Systeme:
sie können die Win32-API-Funktionen FindFirstFile / FindNextFile / FindClose verwenden.
Das folgende C++ - Beispiel zeigt eine minimale Verwendung von 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);
}
}
Quellcode von den obigen Msdn-Seiten.
Eine Funktion genügt, Sie müssen keine Drittanbieter-Bibliothek (für Windows) verwenden.
#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: Wie von @Sebastian erwähnt, können Sie *.*
in *.ext
ändern, um nur die EXT-Dateien (d. H. Eines bestimmten Typs) in diesem Verzeichnis zu erhalten.
Für eine reine C-Lösung überprüfen Sie dies bitte. Es ist nur ein zusätzlicher Header erforderlich:
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);
Einige Vorteile gegenüber anderen Optionen:
readdir_r
, was bedeutet, dass es normalerweise threadsicher istUNICODE
-MakrosIch empfehle die Verwendung von glob
mit diesem wiederverwendbaren Wrapper. Es generiert einen vector<string>
, der den Dateipfaden entspricht, die in das Glob-Muster passen:
#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;
}
Das kann dann mit einem normalen System-Platzhaltermuster aufgerufen werden, z.
vector<string> files = globVector("./*");
Hier ist ein sehr einfacher Code in C++11
, der boost::filesystem
-Bibliothek verwendet, um Dateinamen in einem Verzeichnis (mit Ausnahme von Ordnernamen) abzurufen:
#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;
}
}
Ausgabe ist wie:
file1.txt
file2.dat
Warum nicht glob()
verwenden?
#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;
}
Ich denke, der folgende Ausschnitt kann verwendet werden, um alle Dateien aufzulisten.
#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);
}
Nachfolgend ist die Struktur des struct dirent dargestellt
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 */
};
Testen Sie die x-Plattform-Methode
http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm
oder verwenden Sie einfach Ihre betriebssystemspezifischen Dateien.
Schauen Sie sich diese Klasse an, die die Win32-API verwendet. Erstellen Sie einfach eine Instanz, indem Sie die Variable foldername
bereitstellen, aus der Sie die Auflistung erstellen möchten. Rufen Sie dann die Methode getNextFile
auf, um die nächste Variable filename
aus dem Verzeichnis abzurufen. Ich denke es braucht windows.h
und 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;
}
};
GNU-Handbuch FTW
Manchmal ist es auch gut, direkt zur Quelle zu gehen (Wortspiel beabsichtigt). Sie können viel lernen, wenn Sie sich die Innenseiten einiger der häufigsten Befehle in Linux ansehen. Ich habe einen einfachen Spiegel der GNU-Coreutils auf Github (zum Lesen) eingerichtet.
https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c
Vielleicht wird Windows nicht angesprochen, aber es gibt eine Reihe von Fällen, in denen Unix-Varianten verwendet werden.
Hoffentlich hilft das...
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;
}
Ich hoffe, dieser Code hilft dir.
#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;
}
sie können alle direkten Dateien in Ihrem Stammverzeichnis abrufen, indem Sie std :: experimental :: filesystem :: directory_iterator () verwenden. Lesen Sie dann den Namen dieser Pfaddateien.
#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;
}
Das funktioniert für mich. Es tut mir leid, wenn ich mich nicht an die Quelle erinnern kann. Es ist wahrscheinlich von einer Manpage.
#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/");
}
Die Antwort von Shreevardhan funktioniert großartig. Wenn Sie es in c ++ 14 verwenden möchten, nehmen Sie einfach eine Änderung vor namespace fs = experimental::filesystem;
d.h.
#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;
}
Diese Implementierung realisiert Ihren Zweck und füllt dynamisch ein Array von Strings mit dem Inhalt des angegebenen Verzeichnisses.
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;
}
Diese Antwort sollte für Windows-Benutzer funktionieren, die Probleme damit hatten, mit Visual Studio mit einer der anderen Antworten zusammenzuarbeiten.
Laden Sie die Datei dirent.h von der Github-Seite herunter. Es ist jedoch besser, nur die Datei Raw dirent.h zu verwenden und die folgenden Schritte auszuführen (so habe ich es zum Laufen gebracht).
Github-Seite für dirent.h für Windows: Github-Seite für dirent.h
Raw Dirent File: Raw dirent.h File
Gehen Sie zu Ihrem Projekt und fügen Sie ein neues Objekt hinzu (Ctrl+Shift+A). Fügen Sie eine Header-Datei (.h) hinzu und nennen Sie sie dirent.h.
Fügen Sie den Code Raw dirent.h File in Ihre Kopfzeile ein.
Fügen Sie "dirent.h" in Ihren Code ein.
Fügen Sie die folgende Methode void filefinder()
in Ihren Code ein und rufen Sie sie von Ihrer Funktion main
aus auf, oder bearbeiten Sie die Funktion, wie Sie sie verwenden möchten.
#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);
}
System nennen es!
system( "dir /b /s /a-d * > file_names.txt" );
Dann lesen Sie einfach die Datei.
BEARBEITEN: Diese Antwort sollte als Hack angesehen werden, funktioniert jedoch (wenn auch plattformspezifisch), wenn Sie nicht auf elegantere Lösungen zugreifen können.
Ich habe versucht, dem Beispiel in bothanswers zu folgen, und es lohnt sich zu beachten, dass std::filesystem::directory_entry
so geändert wurde, dass der <<
-Operator nicht überladen wird. Anstelle von std::cout << p << std::endl;
musste ich Folgendes verwenden, um kompilieren und funktionieren zu können:
#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;
}
der Versuch, p
alleine an std::cout <<
zu übergeben, führte zu einem fehlenden Überlastungsfehler.
Da Dateien und Unterverzeichnisse eines Verzeichnisses im Allgemeinen in einer Baumstruktur gespeichert werden, ist die Verwendung des DFS-Algorithmus eine intuitive Methode, um jedes Verzeichnis rekursiv zu durchlaufen. Hier ist ein Beispiel für ein Windows-Betriebssystem, in dem grundlegende Dateifunktionen in io.h verwendet werden. Sie können diese Funktionen auf einer anderen Plattform ersetzen. Ich möchte ausdrücken, dass die Grundidee von DFS dieses Problem perfekt erfüllt.
#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);
}
Nur etwas, das ich teilen möchte und danke Ihnen für das Lesematerial. Spielen Sie ein wenig mit der Funktion, um sie zu verstehen. Vielleicht gefällt es dir. e stand für die Erweiterung, p steht für Pfad und s steht für Pfadtrennzeichen.
Wenn der Pfad ohne Trennzeichen abgeschlossen wird, wird ein Trennzeichen an den Pfad angehängt. Wenn für die Erweiterung eine leere Zeichenfolge eingegeben wird, gibt die Funktion eine Datei zurück, die keine Erweiterung im Namen hat. Wenn ein einzelner Stern eingegeben wurde, werden alle Dateien im Verzeichnis zurückgegeben. Wenn die Länge von e größer als 0 ist, aber kein einzelnes * ist, wird ein Punkt vor e gesetzt, wenn e keinen Punkt an der Nullposition enthält.
Für einen wiederkehrenden Wert. Wenn eine Karte mit einer Länge von null zurückgegeben wird, wurde nichts gefunden, aber das Verzeichnis war in Ordnung. Wenn der Rückgabewert Index 999 enthält, die Kartengröße jedoch nur 1 ist, bedeutet dies, dass beim Öffnen des Verzeichnispfads ein Problem aufgetreten ist.
Beachten Sie, dass diese Funktion aus Gründen der Effizienz in 3 kleinere Funktionen unterteilt werden kann. Darüber hinaus können Sie eine Anruferfunktion erstellen, die anhand der Eingabe ermittelt, welche Funktion sie aufrufen wird. Warum ist das effizienter? Wenn Sie also alles, was eine Datei ist, packen wollen, so wird die Unterfunktion, die zum Abrufen aller Dateien erstellt wurde, nur alle Dateien übernehmen und muss nicht jedes Mal, wenn eine Datei gefunden wurde, eine andere unnötige Bedingung auswerten.
Dies gilt auch für Dateien, die keine Erweiterung haben. Eine speziell für diesen Zweck erstellte Funktion würde nur dann nach Wetterlage suchen, wenn das gefundene Objekt eine Datei ist und dann, ob der Name der Datei einen Punkt enthält.
Das Speichern ist möglicherweise nicht viel, wenn Sie nur Verzeichnisse mit weniger Dateien lesen. Wenn Sie jedoch eine große Anzahl von Verzeichnissen lesen oder wenn das Verzeichnis mehrere hunderttausend Dateien enthält, kann dies eine große Ersparnis bedeuten.
#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;
}