Il modo più veloce per verificare se esiste un file usando C ++ / C ++ 11 / C standard?


453

Vorrei trovare il modo più veloce per verificare se esiste un file in standard C ++ 11, C ++ o C. Ho migliaia di file e prima di fare qualcosa su di essi devo controllare se esistono tutti. Cosa posso scrivere invece che /* SOMETHING */nella seguente funzione?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}

2
boost::filesystemsembra usare stat(). (Supponendo dalla documentazione.) Non credo che si possa fare molto più velocemente per le chiamate FS. Il modo per rendere veloce ciò che stai facendo è "evitare di guardare migliaia di file".
millimoose,

16
Domanda TOCTOU : come fai a sapere se il file non è scollegato tra il tuo controllo esiste () e il tuo "fare qualcosa su di esso" ?
Pilcrow

7
@pilcrow Un buon punto, ma esiste una gamma abbastanza ampia di applicazioni che non richiedono molta correttezza. Ad esempio, git pushprobabilmente non si preoccupa di assicurarsi di non toccare l'albero di lavoro dopo il controllo sporco iniziale.
millimoose

9
'Non riesco a pensare a un'implementazione C / C ++ che non l'avrebbe' - Windows non fornisce un ambiente POSIX.
Jim Balter,

Risposte:


778

Bene, ho messo insieme un programma di test che ha eseguito ciascuno di questi metodi 100.000 volte, metà su file esistenti e metà su file che non lo erano.

#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); 
}

Risultati per il tempo totale necessario per eseguire le 100.000 chiamate calcolate in media su 5 sessioni,

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**

La stat()funzione ha fornito le migliori prestazioni sul mio sistema (Linux, compilato con g++), con una fopenchiamata standard che è la soluzione migliore se per qualche motivo si rifiuta di utilizzare le funzioni POSIX.


31
Nessuno dei metodi di cui sopra verifica l'esistenza, ma piuttosto l'accessibilità. Tuttavia, non conosco un unico metodo C o C ++ standard per verificarne l'esistenza.
Indispensabile il

10
stat()sembra verificare l'esistenza.
el.pescado,

105
Chiunque usi questo deve ricordare di #include <sys / stat.h> altrimenti prova a usare la stat sbagliata.
Katianie,

23
Immagino per il metodo ifstream, non è necessario f.close()poiché f esce dall'ambito alla fine della funzione. Quindi return f.good()potrebbe sostituire il ifblocco?
ilent2,

11
Puoi anche usare / test en.cppreference.com/w/cpp/experimental/fs/exists dal prossimo standard
zahir,

153

Nota: in C ++ 14 e non appena il filesystem TS sarà terminato e adottato, la soluzione sarà quella di utilizzare:

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

e dal C ++ 17, solo:

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

5
già disponibile in Boost.Filesystem
TemplateRex

1
In MS Visual Studio 2013 questa funzione è disponibile instd::tr2::sys::exists("helloworld.txt");
Constantin

3
Spero davvero che non lo sia std::exists, sarebbe piuttosto confuso (pensa: esiste in un contenitore STL come un set).
einpoklum,

3
Anche in Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile,

1
Non dimenticare di#include <experimental/filesystem>
Mohammed Noureldin,

112

Uso questo pezzo di codice, finora funziona bene con me. Questo non usa molte fantasiose funzionalità di C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}

8
Tuttavia, potrebbe non riuscire se il file è stato bloccato da un altro programma o se non è possibile accedere al file.
Jet

2
devi chiudere il flusso?
Mo0gles,

29
@ Mo0gles: il ifstreamdistruttore verrà chiamato all'uscita is_file_existe chiuderà il flusso.
Isaac,

2
A partire da C ++ 11 potresti farlo in una riga usando l'operatore bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen,

6
@Orwellophilereturn std::ifstream(fileName);
emlai,

27

Dipende da dove risiedono i file. Ad esempio, se si suppone che siano tutti nella stessa directory, è possibile leggere tutte le voci della directory in una tabella hash e quindi controllare tutti i nomi rispetto alla tabella hash. Questo potrebbe essere più veloce su alcuni sistemi rispetto al controllo di ciascun file singolarmente. Il modo più veloce per controllare ogni file individualmente dipende dal tuo sistema ... se stai scrivendo ANSI C, il modo più veloce è fopenperché è l'unico modo (un file potrebbe esistere ma non essere apribile, ma probabilmente vuoi davvero aprirlo se bisogno di "fare qualcosa"). C ++, POSIX, Windows offrono tutte opzioni aggiuntive.

Mentre ci sono, vorrei evidenziare alcuni problemi con la tua domanda. Dici che vuoi il modo più veloce e che hai migliaia di file, ma poi chiedi il codice per una funzione per testare un singolo file (e quella funzione è valida solo in C ++, non in C). Questo contraddice le tue esigenze facendo un presupposto sulla soluzione ... un caso del problema XY . Dici anche "nello standard c ++ 11 (o) c ++ (o) c" ... che sono tutti diversi, e questo è anche incompatibile con il tuo requisito di velocità ... la soluzione più veloce implicherebbe l'adattamento del codice al sistema di destinazione. L'incoerenza nella domanda è evidenziata dal fatto che hai accettato una risposta che fornisce soluzioni che dipendono dal sistema e non sono standard C o C ++.


25

Per coloro a cui piace boost:

 boost::filesystem::exists(fileName)

5
L'aumento è di solito estremamente lento.
Serge Rogatch,

4
Per la maggior parte delle applicazioni esiste un controllo del file non critico per le prestazioni
anhoppe

29
Non tutti gli aspetti di un'applicazione ad alte prestazioni richiedono ottimizzazione. Ad esempio, leggere la riga di comando o un file di configurazione può essere complesso e potrebbe non richiedere velocità, sebbene l'applicazione stessa possa richiedere i vantaggi in termini di prestazioni di C ++. Evitare Boost in questi casi costituisce una reinvenzione delle ruote, in cima alla lista degli anti-pattern.
evoskuil,

5
Il boost di @SergeRogatch :: filesystem :: esiste non è estremamente lento. Vedi i risultati del mio benchmark per informazioni dettagliate.
Hungptit

3
"Boost è di solito estremamente lento" - questo è falso, e non è nemmeno chiaro quale sia l'ambito del reclamo ... Boost contiene molti pacchetti di autori diversi ma è controllato per l'alta qualità. "Per la maggior parte delle applicazioni un controllo dei file non è critico per le prestazioni" - l'OP ha richiesto specificamente la velocità a causa del controllo di un numero molto elevato di file. "Se le prestazioni non sono critiche, allora non ha senso usare C ++" - un altro commento errato (e fuori tema). La maggior parte dei software è scritta nei negozi e fa parte di un sistema che impone la scelta della lingua.
Jim Balter,

23

Senza usare altre librerie, mi piace usare il seguente frammento di codice:

#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;
}

Funziona su più piattaforme per sistemi compatibili con Windows e POSIX.


Funziona su Mac? Non ho un Mac, ma mi aspetto che anche un Mac possa includerlo unistd.h. Forse il primo #ifdefdovrebbe essere specifico di Windows?
matth,

5
Mac OSX è conforme a POSIX.
Schaiba,

20

Come suggerito da PherricOxide ma in C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}

1
.c_str () è una funzione C ++. Non conosco C ++ quindi ho pubblicato un equivalente C.
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.
}

19
Se lo farai davvero, basta "restituire (bool) file" invece di usare un ramo if / else.
Nik Haldimann,

Non dimenticare di chiudere il file nel caso vero. Questo è un tipo di perdita di memoria se si lascia il file aperto per l'intero runtime del programma, per non parlare del fatto che potrebbe bloccare il file in modo da non poterlo leggere dopo aver saputo che esiste .. add: file.close () secondo altro.
Bill Moore,

2
ripensandoci, forse non è necessario chiuderlo esplicitamente ... Ho dimenticato che ifstream è un RAII (Resource Acquisition Is Initialization) ... e si pulirà mentre esce dal campo di applicazione dal distruttore ... posso dire ... Mi fanno il lavaggio del cervello dalle lingue dei raccoglitori di rifiuti in questi giorni ...
Bill Moore,

@BillMoore Il tuo secondo commento è corretto; molti altri commenti su questa pagina hanno notato che close()non è necessario.
Keith M,

Questo controlla l'accessibilità, non l'esistenza. Ad esempio, se il file esiste, ma non è possibile accedervi a causa dei diritti di accesso, restituirà false, affermando erroneamente che il file non esiste.
SasQ,

7

Altre 3 opzioni sotto 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;
}

OpenFile è solo ANSI e limitato a 128 caratteri .
David Bremner,

5
La GetFileAttributesversione è fondamentalmente il modo canonico per farlo in Windows.
Felix Dombek,

So che questo è vecchio, ma cosa accadrà nel terzo caso quando l'utente ha la possibilità di leggere il file ma non è autorizzato a leggere gli attributi del file?
Quest

6

Puoi anche farlo bool b = std::ifstream('filename').good();. Senza le istruzioni di diramazione (come se), deve funzionare più velocemente in quanto deve essere chiamato migliaia di volte.


Come mostra la risposta accettata, questo non è vero. Qualsiasi compilatore serio probabilmente emetterà lo stesso codice, indipendentemente dal fatto che tu abbia inserito il if . Rispetto alle varianti C semplici, la costruzione dell'oggetto ifstream (anche se in pila) comporta un sovraccarico aggiuntivo.
minexew,

5

Se è necessario distinguere tra un file e una directory, considerare quanto segue che utilizza sia stat che lo strumento standard più veloce, come dimostrato da PherricOxide:

#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;
}

4

Ho bisogno di una funzione veloce in grado di verificare se esiste un file o meno e la risposta di PherricOxide è quasi ciò di cui ho bisogno, tranne che non confronta le prestazioni di boost :: filesystem :: esiste e funzioni aperte. Dai risultati del benchmark possiamo facilmente vedere che:

  • L'uso della funzione stat è il modo più rapido per verificare l'esistenza di un file. Nota che i miei risultati sono coerenti con quelli della risposta di PherricOxide.

  • Le prestazioni della funzione boost :: filesystem :: esiste sono molto simili a quelle della funzione stat ed è anche portatile. Consiglierei questa soluzione se le librerie boost sono accessibili dal tuo codice.

Risultati benchmark ottenuti con kernel Linux 4.17.0 e 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

Di seguito è riportato il mio codice di riferimento:

#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();   

4

Puoi usare std::ifstreamfunzioni come is_open, failad esempio come sotto il codice (il cout "aperto" significa che il file esiste o no):

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

citato da questa risposta


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

dov'è la Rtua sequenza di cose simili a percorsi, e exists()proviene dal futuro std o boost attuale. Se arrotoli il tuo, mantienilo semplice,

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

La soluzione ramificata non è assolutamente terribile e non inghiottirà i descrittori di file,

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
}

PathFileExistsè limitato a MAX_PATH(260) caratteri; GetFileAttributesnon ha questa limitazione.
Felix Dombek,

GetFileAttributesè limitato anche a MAX_PATH. I documenti descrivono una soluzione alternativa se si utilizzano percorsi assoluti, Unicode e si antepone una stringa di prefisso speciale al nome del percorso. Penso che siamo comunque fuori di testa con le risposte specifiche di Windows.
Giovanni

1
GetFileAttributesWnon ha il limite.
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);
}

5
Questo è meno informativo della risposta data da Vincent 4 anni prima.
Jim Balter,

2
Nel file system C ++ 17 non è più sperimentale
Quest

0

Utilizzando MFC è possibile con quanto segue

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

Dov'è FileNameuna stringa che rappresenta il file che stai verificando per l'esistenza


0

c'è solo un modo più veloce per verificare se il file esiste e se hai il permesso di leggerlo il modo in cui usa il linguaggio C è più veloce e può essere usato anche in qualsiasi versione in C ++

soluzione : in C c'è una libreria errno.h che ha una variabile intera (globale) esterna chiamata errno che contiene un numero che può essere usato per riconoscere il tipo di errore

    #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;
    }

-4

Sebbene ci siano diversi modi per farlo, la soluzione più efficiente al tuo problema sarebbe probabilmente quella di utilizzare uno dei metodi predefiniti del flusso come good () . Con questo metodo puoi verificare se il file che hai specificato esiste o meno.

fstream file("file_name.txt");

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

Spero che lo trovi utile


4
Questo codice creerà il file se non esiste, quindi il risultato sarà sempre vero. È necessario utilizzare ifstream o impostare correttamente il parametro openmode.
Lubo Antonov,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.