Der schnellste Weg, um zu überprüfen, ob eine Datei mit Standard C ++ / C ++ 11 / C vorhanden ist?

453

Ich möchte den schnellsten Weg finden, um zu überprüfen, ob eine Datei in Standard C ++ 11, C ++ oder C vorhanden ist. Ich habe Tausende von Dateien und bevor ich etwas daran mache, muss ich überprüfen, ob alle vorhanden sind. Was kann ich anstelle /* SOMETHING */der folgenden Funktion schreiben ?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Vincent
quelle
2
boost::filesystemscheint zu verwenden stat(). (Vorausgesetzt aus der Dokumentation.) Ich glaube nicht, dass Sie für FS-Anrufe viel schneller arbeiten können. Der Weg, um das, was Sie tun, schnell zu machen, besteht darin, "Tausende von Dateien nicht anzusehen".
Millimoose
16
TOCTOU- Frage: Woher wissen Sie, dass die Datei nicht zwischen Ihrer existierenden () Prüfung und Ihrem "etwas dagegen tun" nicht verknüpft ist ?
Pilcrow
7
@pilcrow Guter Punkt, aber es gibt eine ziemlich breite Palette von Anwendungen, die nicht so viel Korrektheit benötigen. ZB macht sich git pushwahrscheinlich nicht die Mühe, sicherzustellen, dass Sie den Arbeitsbaum nach der ersten schmutzigen Prüfung nicht berühren.
Millimoose
9
"Ich kann mir keine C / C ++ - Implementierung vorstellen, die es nicht hätte" - Windows bietet keine POSIX-Umgebung.
Jim Balter

Antworten:

778

Nun, ich habe ein Testprogramm zusammengestellt, das jede dieser Methoden 100.000 Mal ausführte, je zur Hälfte für vorhandene Dateien und zur Hälfte für nicht vorhandene Dateien.

#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <fstream>

inline bool exists_test0 (const std::string& name) {
    ifstream f(name.c_str());
    return f.good();
}

inline bool exists_test1 (const std::string& name) {
    if (FILE *file = fopen(name.c_str(), "r")) {
        fclose(file);
        return true;
    } else {
        return false;
    }   
}

inline bool exists_test2 (const std::string& name) {
    return ( access( name.c_str(), F_OK ) != -1 );
}

inline bool exists_test3 (const std::string& name) {
  struct stat buffer;   
  return (stat (name.c_str(), &buffer) == 0); 
}

Ergebnisse für die Gesamtzeit zum Ausführen der 100.000 Anrufe, gemittelt über 5 Läufe,

Method exists_test0 (ifstream): **0.485s**
Method exists_test1 (FILE fopen): **0.302s**
Method exists_test2 (posix access()): **0.202s**
Method exists_test3 (posix stat()): **0.134s**

Die stat()Funktion bot die beste Leistung auf meinem System (Linux, kompiliert mit g++), wobei ein Standardaufruf fopendie beste Wahl ist, wenn Sie aus irgendeinem Grund die Verwendung von POSIX-Funktionen ablehnen.

PherricOxide
quelle
31
Keine der oben genannten Methoden prüft die Existenz, sondern die Zugänglichkeit. Ich kenne jedoch keinen einzigen Standard-C- oder C ++ - Weg, um die Existenz zu überprüfen.
Unsichtbarer
10
stat()scheint auf Existenz zu prüfen.
el.pescado
105
Jeder, der dies verwendet, muss daran denken, #include <sys / stat.h> zu verwenden, da er sonst versucht, den falschen stat zu verwenden.
Katianie
23
Ich stelle mir vor, dass Sie für die ifstream-Methode nicht benötigen, f.close()da f am Ende der Funktion den Gültigkeitsbereich verlässt . So return f.good()könnte der Ersatz - ifBlock?
ilent2
11
Sie können auch / test en.cppreference.com/w/cpp/experimental/fs/exists vom kommenden Standard verwenden
zahir
153

Anmerkung: In C ++ 14 und sobald das Dateisystem TS fertiggestellt und übernommen ist, besteht die Lösung darin, Folgendes zu verwenden:

std::experimental::filesystem::exists("helloworld.txt");

und seit C ++ 17 nur:

std::filesystem::exists("helloworld.txt");
Vincent
quelle
5
bereits verfügbar in Boost.Filesystem
TemplateRex
1
In MS Visual Studio 2013 ist diese Funktion unterstd::tr2::sys::exists("helloworld.txt");
Constantin
3
Ich hoffe tatsächlich std::exists, dass dies nicht der Fall ist, was ziemlich verwirrend wäre (denken Sie: existiert in einem STL-Container wie einem Set).
Einpoklum
3
Auch in Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile
1
Vergessen Sie nicht#include <experimental/filesystem>
Mohammed Noureldin
112

Ich benutze diesen Code, er funktioniert bei mir bisher einwandfrei. Dies nutzt nicht viele ausgefallene Funktionen von C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
harryngh
quelle
8
Es kann jedoch fehlschlagen, wenn die Datei von einem anderen Programm gesperrt wurde oder wenn kein Zugriff auf die Datei besteht.
Jet
2
Müssen Sie den Stream schließen?
Mo0gles
29
@ Mo0gles: Der ifstreamDestruktor wird beim Beenden aufgerufen is_file_existund schließt den Stream.
Isaac
2
Ab C ++ 11 können Sie dies in einer Zeile mit dem Bool-Operator tun
Mugen
6
@Orwellophilereturn std::ifstream(fileName);
Emlai
27

Dies hängt davon ab, wo sich die Dateien befinden. Wenn sich beispielsweise alle im selben Verzeichnis befinden sollen, können Sie alle Verzeichniseinträge in eine Hash-Tabelle einlesen und dann alle Namen mit der Hash-Tabelle vergleichen. Dies könnte schneller auf manchen Systemen als jede Datei einzeln zu prüfen. Der schnellste Weg, jede Datei einzeln zu überprüfen, hängt von Ihrem System ab. Wenn Sie ANSI C schreiben, ist der schnellste Weg, fopenweil dies der einzige Weg ist (eine Datei ist möglicherweise vorhanden, aber nicht zu öffnen, aber Sie möchten sie wahrscheinlich wirklich öffnen, wenn Sie dies tun müssen "etwas darauf tun"). C ++, POSIX und Windows bieten zusätzliche Optionen.

Lassen Sie mich, während ich dabei bin, auf einige Probleme mit Ihrer Frage hinweisen. Sie sagen, dass Sie den schnellsten Weg wollen und dass Sie Tausende von Dateien haben, aber dann fragen Sie nach dem Code für eine Funktion zum Testen einer einzelnen Datei (und diese Funktion ist nur in C ++ gültig, nicht in C). Dies widerspricht Ihren Anforderungen, indem Sie eine Annahme über die Lösung treffen ... ein Fall des XY-Problems . Sie sagen auch "in Standard c ++ 11 (oder) c ++ (oder) c" ... die alle unterschiedlich sind, und dies steht auch im Widerspruch zu Ihrer Anforderung an die Geschwindigkeit ... die schnellste Lösung würde darin bestehen, den Code an das anzupassen Zielsystem. Die Inkonsistenz in der Frage wird durch die Tatsache hervorgehoben, dass Sie eine Antwort akzeptiert haben, die systemabhängige Lösungen enthält, die nicht Standard C oder C ++ sind.

Jim Balter
quelle
25

Für diejenigen, die Boost mögen:

 boost::filesystem::exists(fileName)
anhoppe
quelle
5
Boost ist normalerweise extrem langsam.
Serge Rogatch
4
Für die meisten Anwendungen ist eine Datei vorhanden. Die Überprüfung ist nicht leistungskritisch
anhoppe
29
Nicht alle Aspekte einer Hochleistungsanwendung müssen optimiert werden. Das Lesen der Befehlszeile oder einer Konfigurationsdatei kann beispielsweise komplex sein und erfordert möglicherweise keine Geschwindigkeit, obwohl die Anwendung selbst möglicherweise die Leistungsvorteile von C ++ erfordert. Das Vermeiden von Boost in solchen Fällen stellt eine Neuerfindung der Räder dar, die ganz oben auf der Anti-Pattern-Liste steht.
Evoskuil
5
@ SergeRogatch boost :: filesystem :: exist ist nicht extrem langsam. Weitere Informationen finden Sie in meinen Benchmark-Ergebnissen.
hungptit
3
"Boost ist normalerweise extrem langsam" - das ist falsch und es ist nicht einmal klar, was der Umfang der Behauptung ist ... Boost enthält viele Pakete von verschiedenen Autoren, ist aber auf hohe Qualität überprüft. "Für die meisten Anwendungen ist eine Datei vorhanden. Die Überprüfung ist nicht leistungskritisch." Das OP forderte speziell die Geschwindigkeit an, da eine sehr große Anzahl von Dateien überprüft wurde. "Wenn die Leistung nicht kritisch ist, macht es auch keinen Sinn, C ++ zu verwenden" - ein weiterer fehlerhafter Kommentar (und kein Thema). Die meiste Software wird in Geschäften geschrieben und ist Teil eines Systems , das die Wahl der Sprache vorschreibt.
Jim Balter
23

Ohne andere Bibliotheken zu verwenden, verwende ich gerne das folgende Code-Snippet:

#ifdef _WIN32
   #include <io.h> 
   #define access    _access_s
#else
   #include <unistd.h>
#endif

bool FileExists( const std::string &Filename )
{
    return access( Filename.c_str(), 0 ) == 0;
}

Dies funktioniert plattformübergreifend für Windows- und POSIX-kompatible Systeme.

Viktor Liehr
quelle
Funktioniert das auf dem Mac? Ich habe keinen Mac, aber ich würde erwarten, dass ein Mac unistd.hauch enthalten kann. Vielleicht sollte der erste #ifdefWindows-spezifisch sein?
Matth
5
Mac OSX ist POSIX-kompatibel.
schaiba
20

Wie von PherricOxide vorgeschlagen, jedoch in C.

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}
Ramon La Pietra
quelle
1
.c_str () ist eine C ++ - Funktion. Ich kenne C ++ nicht, also habe ich ein C-Äquivalent gepostet.
Ramon La Pietra
10
inline bool exist(const std::string& name)
{
    ifstream file(name);
    if(!file)            // If the file was not found, then file is 0, i.e. !file=1 or true.
        return false;    // The file was not found.
    else                 // If the file was found, then file is non-0.
        return true;     // The file was found.
}
LOLOLOL
quelle
19
Wenn Sie das wirklich tun wollen, geben Sie einfach "return (bool) file" zurück, anstatt einen if / else-Zweig zu verwenden.
Nik Haldimann
Vergessen Sie nicht, die Datei im Ernstfall zu schließen. Dies ist eine Art Speicherverlust, wenn Sie die Datei für die gesamte Laufzeit des Programms offen lassen. Außerdem wird Ihre Datei möglicherweise gesperrt, sodass Sie sie nicht lesen können, nachdem Sie wissen, dass sie vorhanden ist. Add: file.close () zu zweit sonst.
Bill Moore
2
Beim zweiten Gedanken müssen Sie es vielleicht nicht explizit schließen ... Ich habe vergessen, dass ifstream ein RAII (Resource Acquisition Is Initialization) ist ... und sich selbst bereinigt, wenn es vom Destruktor aus dem Bereich gerät ... was Kann ich sagen ... Ich werde heutzutage von Müllsammlersprachen einer Gehirnwäsche
Bill Moore
@ BillMoore Dein zweiter Kommentar ist korrekt; Viele andere Kommentare auf dieser Seite haben festgestellt, dass dies close()nicht erforderlich ist.
Keith M
Dies überprüft die Zugänglichkeit, nicht die Existenz. Wenn die Datei beispielsweise vorhanden ist, aber aufgrund von Zugriffsrechten nicht auf sie zugegriffen werden kann, wird false zurückgegeben, und es wird fälschlicherweise behauptet, dass die Datei nicht vorhanden ist.
SasQ
7

Weitere 3 Optionen unter Windows:

1

inline bool exist(const std::string& name)
{
    OFSTRUCT of_struct;
    return OpenFile(name.c_str(), &of_struct, OF_EXIST) != INVALID_HANDLE_VALUE && of_struct.nErrCode == 0;
}

2

inline bool exist(const std::string& name)
{
    HANDLE hFile = CreateFile(name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != NULL && hFile != INVALID_HANDLE)
    {
         CloseFile(hFile);
         return true;
    }
    return false;
}

3

inline bool exist(const std::string& name)
{
    return GetFileAttributes(name.c_str()) != INVALID_FILE_ATTRIBUTES;
}
ravin.wang
quelle
OpenFile ist nur ANSI und auf 128 Zeichen begrenzt .
David Bremner
5
Die GetFileAttributesVersion ist im Grunde die kanonische Methode, um dies in Windows zu tun.
Felix Dombek
Ich weiß, dass dies alt ist, aber was passiert im dritten Fall, wenn der Benutzer die Datei lesen kann, aber die Dateiattribute nicht lesen darf?
Quest
6

Sie können auch tun bool b = std::ifstream('filename').good();. Ohne die Verzweigungsanweisungen (wie wenn) muss es schneller arbeiten, da es tausende Male aufgerufen werden muss.

parv
quelle
Wie die akzeptierte Antwort zeigt, ist dies nicht wahr. Jeder seriöse Compiler wird wahrscheinlich denselben Code ausgeben, unabhängig davon, ob Sie das if eingeben oder nicht. Im Vergleich zu den Plain-C-Varianten verursacht das Erstellen des ifstream-Objekts (auch wenn es sich auf dem Stapel befindet) zusätzlichen Aufwand.
Minexew
5

Wenn Sie zwischen einer Datei und einem Verzeichnis unterscheiden müssen, beachten Sie Folgendes: Beide verwenden stat, das schnellste Standardwerkzeug, wie von PherricOxide gezeigt:

#include <sys/stat.h>
int FileExists(char *path)
{
    struct stat fileStat; 
    if ( stat(path, &fileStat) )
    {
        return 0;
    }
    if ( !S_ISREG(fileStat.st_mode) )
    {
        return 0;
    }
    return 1;
}

int DirExists(char *path)
{
    struct stat fileStat;
    if ( stat(path, &fileStat) )
    {
        return 0;
    }
    if ( !S_ISDIR(fileStat.st_mode) )
    {
        return 0;
    }
    return 1;
}
user3902302
quelle
4

Ich brauche eine schnelle Funktion, die überprüfen kann, ob eine Datei vorhanden ist oder nicht, und die Antwort von PherricOxide ist fast das, was ich brauche, außer dass sie die Leistung von boost :: filesystem :: exist und offenen Funktionen nicht vergleicht. An den Benchmark-Ergebnissen können wir leicht erkennen, dass:

  • Die Verwendung der Statistikfunktion ist der schnellste Weg, um zu überprüfen, ob eine Datei vorhanden ist. Beachten Sie, dass meine Ergebnisse mit denen der Antwort von PherricOxide übereinstimmen.

  • Die Leistung der Funktion boost :: filesystem :: existiert kommt der der Funktion stat sehr nahe und ist auch portabel. Ich würde diese Lösung empfehlen, wenn Boost-Bibliotheken über Ihren Code zugänglich sind.

Benchmark-Ergebnisse mit Linux-Kernel 4.17.0 und gcc-7.3:

2018-05-05 00:35:35
Running ./filesystem
Run on (8 X 2661 MHz CPU s)
CPU Caches:
  L1 Data 32K (x4)
  L1 Instruction 32K (x4)
  L2 Unified 256K (x4)
  L3 Unified 8192K (x1)
--------------------------------------------------
Benchmark           Time           CPU Iterations
--------------------------------------------------
use_stat          815 ns        813 ns     861291
use_open         2007 ns       1919 ns     346273
use_access       1186 ns       1006 ns     683024
use_boost         831 ns        830 ns     831233

Unten ist mein Benchmark-Code:

#include <string.h>                                                                                                                                                                                                                                           
#include <stdlib.h>                                                                                                                                                                                                                                           
#include <sys/types.h>                                                                                                                                                                                                                                        
#include <sys/stat.h>                                                                                                                                                                                                                                         
#include <unistd.h>                                                                                                                                                                                                                                           
#include <dirent.h>                                                                                                                                                                                                                                           
#include <fcntl.h>                                                                                                                                                                                                                                            
#include <unistd.h>                                                                                                                                                                                                                                           

#include "boost/filesystem.hpp"                                                                                                                                                                                                                               

#include <benchmark/benchmark.h>                                                                                                                                                                                                                              

const std::string fname("filesystem.cpp");                                                                                                                                                                                                                    
struct stat buf;                                                                                                                                                                                                                                              

// Use stat function                                                                                                                                                                                                                                          
void use_stat(benchmark::State &state) {                                                                                                                                                                                                                      
    for (auto _ : state) {                                                                                                                                                                                                                                    
        benchmark::DoNotOptimize(stat(fname.data(), &buf));                                                                                                                                                                                                   
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_stat);                                                                                                                                                                                                                                          

// Use open function                                                                                                                                                                                                                                          
void use_open(benchmark::State &state) {                                                                                                                                                                                                                      
    for (auto _ : state) {                                                                                                                                                                                                                                    
        int fd = open(fname.data(), O_RDONLY);                                                                                                                                                                                                                
        if (fd > -1) close(fd);                                                                                                                                                                                                                               
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_open);                                  
// Use access function                                                                                                                                                                                                                                        
void use_access(benchmark::State &state) {                                                                                                                                                                                                                    
    for (auto _ : state) {                                                                                                                                                                                                                                    
        benchmark::DoNotOptimize(access(fname.data(), R_OK));                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_access);                                                                                                                                                                                                                                        

// Use boost                                                                                                                                                                                                                                                  
void use_boost(benchmark::State &state) {                                                                                                                                                                                                                     
    for (auto _ : state) {                                                                                                                                                                                                                                    
        boost::filesystem::path p(fname);                                                                                                                                                                                                                     
        benchmark::DoNotOptimize(boost::filesystem::exists(p));                                                                                                                                                                                               
    }                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                             
BENCHMARK(use_boost);                                                                                                                                                                                                                                         

BENCHMARK_MAIN();   
hungptit
quelle
4

Sie können verwendet werden std::ifstream, funcion wie is_open, failzum Beispiel , wie unten Code (der cout „offen“ bedeutet Datei existiert oder nicht):

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

zitiert aus dieser Antwort

Jayhello
quelle
3
all_of (begin(R), end(R), [](auto&p){ exists(p); })

Wo Rist deine Abfolge von pfadartigen Dingen und exists()ist von der zukünftigen Standard- oder aktuellen Boost. Wenn Sie Ihre eigenen rollen, halten Sie es einfach,

bool exists (string const& p) { return ifstream{p}; }

Die verzweigte Lösung ist nicht absolut schrecklich und verschlingt keine Dateideskriptoren.

bool exists (const char* p) {
    #if defined(_WIN32) || defined(_WIN64)
    return p && 0 != PathFileExists (p);
    #else
    struct stat sb;
    return p && 0 == stat (p, &sb);
    #endif
}
John
quelle
PathFileExistsist auf MAX_PATH(260) Zeichen begrenzt;GetFileAttributeshat diese Einschränkung nicht.
Felix Dombek
GetFileAttributesist ebenfalls auf MAX_PATH beschränkt. In den Dokumenten wird eine Problemumgehung beschrieben, wenn Sie absolute Pfade und Unicode verwenden und dem Pfadnamen eine spezielle Präfixzeichenfolge voranstellen. Ich denke, wir haben es sowieso mit den Windows-spezifischen Antworten zu tun.
John
1
GetFileAttributesWhat nicht die Einschränkung.
Laurie Stearn
1

In C ++ 17:

#include <experimental/filesystem>

bool is_file_exist(std::string& str) {   
    namespace fs = std::experimental::filesystem;
    fs::path p(str);
    return fs::exists(p);
}
Abhijeet Kandalkar
quelle
5
Dies ist weniger informativ als die Antwort von Vincent vor 4 Jahren.
Jim Balter
2
In C ++ 17 ist das Dateisystem nicht mehr experimentell
Quest
0

Mit MFC ist Folgendes möglich

CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);

Wo FileNameist eine Zeichenfolge, die die Datei darstellt, die Sie auf Existenz prüfen?

Andy Bantly
quelle
0

Es gibt nur einen schnelleren Weg, um zu überprüfen, ob die Datei vorhanden ist, und wenn Sie die Berechtigung zum Lesen haben, ist die Verwendung der C-Sprache schneller und kann auch in jeder Version in C ++ verwendet werden

Lösung : In C gibt es eine Bibliothek errno.h mit einer externen (globalen) Ganzzahlvariablen namens errno, die eine Zahl enthält, mit der der Fehlertyp erkannt werden kann

    #include <stdio.h>
    #include <stdbool.h>
    #include <errno.h>

    bool isFileExist(char fileName[]) {
        FILE *fp = fopen(fileName, "r");
        if (fp) {
            fclose(fp);
            return true;
        }
        return errno != ENOENT;
    }

    bool isFileCanBeRead(char fileName[]) {
        FILE *fp = fopen(fileName, "r");
        if (fp) {
            fclose(fp);
            return true;
        }
        return errno != ENOENT && errno != EPERM;
    }
Imad Diraa
quelle
-4

Obwohl es mehrere Möglichkeiten gibt, dies zu tun, besteht die effizienteste Lösung für Ihr Problem wahrscheinlich darin, eine der vordefinierten Methoden des fstream wie good () zu verwenden . Mit dieser Methode können Sie überprüfen, ob die von Ihnen angegebene Datei vorhanden ist oder nicht.

fstream file("file_name.txt");

if (file.good()) 
{
    std::cout << "file is good." << endl;
}
else 
{
    std::cout << "file isnt good" << endl;
}

Ich hoffe, Sie finden das nützlich.

miksiii
quelle
4
Dieser Code erstellt die Datei, wenn sie nicht vorhanden ist, sodass das Ergebnis immer wahr ist. Sie müssen entweder ifstream verwenden oder den openmode-Parameter korrekt einstellen.
Lubo Antonov