Le moyen le plus rapide de vérifier si un fichier existe en utilisant C ++ / C ++ 11 / C standard?

453

Je voudrais trouver le moyen le plus rapide de vérifier si un fichier existe en standard C ++ 11, C ++ ou C. J'ai des milliers de fichiers et avant de faire quelque chose, je dois vérifier s'ils existent tous. Que puis-je écrire au lieu de /* SOMETHING */dans la fonction suivante?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Vincent
la source
2
boost::filesystemsemble utiliser stat(). (En supposant d'après la documentation.) Je ne pense pas que vous puissiez faire beaucoup plus rapidement pour les appels FS. La façon de faire ce que vous faites rapidement est «d'éviter de regarder des milliers de fichiers».
millimoose
16
Question TOCTOU : comment savez-vous que le fichier n'est pas dissocié entre votre vérification existe () et votre "faire quelque chose" ?
pilcrow
7
@pilcrow Bon point, mais il existe une gamme assez large d'applications qui n'ont pas besoin de beaucoup d'exactitude. Par exemple, git pushne vous embêtez probablement pas à vous assurer que vous ne touchez pas à l'arbre de travail après la vérification initiale.
millimoose
9
«Je ne peux pas penser à une implémentation C / C ++ qui ne l'aurait pas» - Windows ne fournit pas d'environnement POSIX.
Jim Balter
3
Copie
MD XF

Réponses:

778

Eh bien, j'ai créé un programme de test qui exécutait chacune de ces méthodes 100 000 fois, la moitié sur des fichiers qui existaient et l'autre sur des fichiers qui n'existaient pas.

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

Résultats pour le temps total pour exécuter les 100 000 appels en moyenne sur 5 cycles,

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()fonction a fourni les meilleures performances sur mon système (Linux, compilé avec g++), avec un fopenappel standard étant votre meilleur pari si vous refusez pour une raison quelconque d'utiliser les fonctions POSIX.

PherricOxide
la source
31
Aucune des méthodes ci-dessus ne vérifie l'existence, mais plutôt l'accessibilité. Je ne connais pas de méthode C ou C ++ standard unique pour vérifier l'existence, cependant.
IInspectable
10
stat()semble vérifier l'existence.
el.pescado
105
Quiconque l'utilise doit se rappeler de #include <sys / stat.h> sinon il essaie d'utiliser la mauvaise statistique.
Katianie
23
J'imagine que pour la méthode ifstream, vous n'avez pas besoin f.close()car f sort du cadre à la fin de la fonction. Alors return f.good()pourrait remplacer le ifbloc?
ilent2
11
Vous pouvez également utiliser / test en.cppreference.com/w/cpp/experimental/fs/exists de la prochaine norme
zahir
153

Remarque: en C ++ 14 et dès que le système de fichiers TS sera terminé et adopté, la solution sera d'utiliser:

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

et depuis C ++ 17, seulement:

std::filesystem::exists("helloworld.txt");
Vincent
la source
5
déjà disponible dans Boost.Filesystem
TemplateRex
1
Dans MS Visual Studio 2013, cette fonction est disponible sousstd::tr2::sys::exists("helloworld.txt");
Constantin
3
J'espère que ce ne sera pas le cas std::exists, ce serait assez déroutant (pensez: existe dans un conteneur STL comme un ensemble).
einpoklum
3
Aussi dans Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile
1
N'oubliez pas#include <experimental/filesystem>
Mohammed Noureldin
112

J'utilise ce morceau de code, cela fonctionne bien avec moi jusqu'à présent. Cela n'utilise pas de nombreuses fonctionnalités sophistiquées de C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
Harryngh
la source
8
Cependant, il peut échouer si le fichier a été verrouillé par un autre programme ou s'il n'y a pas accès au fichier.
Jet
2
avez-vous besoin de fermer le flux?
Mo0gles
29
@ Mo0gles: Le ifstreamdestructeur sera appelé à la sortie is_file_existet fermera le flux.
Isaac
2
Depuis C ++ 11, vous pouvez le faire en une seule ligne en utilisant l'opérateur bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen
6
@Orwellophilereturn std::ifstream(fileName);
emlai
27

Cela dépend de l'emplacement des fichiers. Par exemple, s'ils sont tous censés se trouver dans le même répertoire, vous pouvez lire toutes les entrées du répertoire dans une table de hachage, puis vérifier tous les noms par rapport à la table de hachage. Cela peut être plus rapide sur certains systèmes que de vérifier chaque fichier individuellement. La façon la plus rapide de vérifier chaque fichier individuellement dépend de votre système ... si vous écrivez ANSI C, la façon la plus rapide est fopenparce que c'est la seule façon (un fichier peut exister mais ne peut pas être ouvert, mais vous voulez probablement vraiment l'ouvrir si vous besoin de "faire quelque chose"). C ++, POSIX, Windows offrent tous des options supplémentaires.

Pendant que j'y suis, permettez-moi de souligner quelques problèmes avec votre question. Vous dites que vous voulez le moyen le plus rapide et que vous avez des milliers de fichiers, mais vous demandez ensuite le code d'une fonction pour tester un seul fichier (et cette fonction n'est valide qu'en C ++, pas en C). Cela contredit vos exigences en faisant une hypothèse sur la solution ... un cas du problème XY . Vous dites également "en standard c ++ 11 (ou) c ++ (ou) c" ... qui sont tous différents, et cela est également incompatible avec votre exigence de vitesse ... la solution la plus rapide consisterait à adapter le code à la système cible. L'incohérence dans la question est mise en évidence par le fait que vous avez accepté une réponse qui donne des solutions qui dépendent du système et ne sont pas du C ou du C ++ standard.

Jim Balter
la source
25

Pour ceux qui aiment le boost:

 boost::filesystem::exists(fileName)
anhoppe
la source
5
Le boost est généralement extrêmement lent.
Serge Rogatch
4
Pour la plupart des applications, un fichier existe, la vérification n'est pas critique en
termes de
29
Tous les aspects d'une application hautes performances ne nécessitent pas d'optimisation. Par exemple, la lecture de la ligne de commande ou d'un fichier de configuration peut être complexe et ne pas nécessiter de vitesse, bien que l'application elle-même puisse nécessiter les avantages de performances de C ++. Éviter Boost dans de tels cas constitue une réinvention des roues, en haut de la liste anti-motif.
evoskuil
5
@SergeRogatch boost :: filesystem :: exist n'est pas extrêmement lent. Voir mes résultats de référence pour des informations détaillées.
Hungptit
3
"Boost est généralement extrêmement lent" - ceci est faux, et il n'est même pas clair quelle est la portée de la revendication ... Boost contient de nombreux packages de différents auteurs mais est approuvé pour sa haute qualité. "Pour la plupart des applications, la vérification d'un fichier n'est pas critique en termes de performances" - l'OP a spécifiquement demandé une vitesse en raison de la vérification d'un très grand nombre de fichiers. "Si les performances ne sont pas critiques, alors il est également inutile d'utiliser C ++" - un autre commentaire erroné (et hors sujet). La plupart des logiciels sont écrits dans les magasins et font partie d'un système qui impose le choix de la langue.
Jim Balter
23

Sans utiliser d'autres bibliothèques, j'aime utiliser l'extrait de code suivant:

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

Cela fonctionne sur plusieurs plates-formes pour les systèmes Windows et compatibles POSIX.

Viktor Liehr
la source
Est-ce que cela fonctionne sur Mac? Je n'ai pas de mac, mais je m'attendrais à ce qu'un mac puisse unistd.haussi l' inclure . Peut-être que le premier #ifdefdevrait être spécifique à Windows?
Matth
5
Mac OSX est compatible POSIX.
schaiba
20

Identique à celle suggérée par PherricOxide mais en C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}
Ramon La Pietra
la source
1
.c_str () est une fonction C ++. Je ne connais pas C ++ donc j'ai posté un équivalent 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.
}
LOLOLOL
la source
19
Si vous voulez vraiment le faire, il vous suffit de "retourner le fichier (bool)" plutôt que d'utiliser une branche if / else.
Nik Haldimann
N'oubliez pas de fermer le dossier en cas de vrai cas. C'est un type de fuite de mémoire si vous laissez le fichier ouvert pendant toute la durée d'exécution du programme, sans oublier qu'il peut verrouiller votre fichier afin que vous ne puissiez pas le lire après avoir su qu'il existe. Add: file.close () à seconde d'autre.
Bill Moore
2
à la réflexion, vous n'avez peut-être pas besoin de le fermer explicitement ... J'ai oublié que ifstream est un RAII (Resource Acquisition Is Initialization) ... et se nettoiera lorsqu'il sort du champ du destructeur ... puis-je dire ... Je me fais laver le cerveau par les langues du ramasse-miettes ces jours-ci ...
Bill Moore
@BillMoore Votre deuxième commentaire est correct; de nombreux autres commentaires sur cette page ont noté que ce close()n'est pas nécessaire.
Keith M
Cela vérifie l'accessibilité, pas l'existence. Par exemple, si le fichier existe mais n'est pas accessible en raison de droits d'accès, il renverra false, affirmant à tort que le fichier n'existe pas.
SasQ
7

3 autres options sous 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
la source
OpenFile est uniquement ANSI et limité à 128 caractères .
David Bremner
5
La GetFileAttributesversion est fondamentalement la manière canonique de le faire dans Windows.
Felix Dombek
Je sais que c'est vieux mais que se passera-t-il dans le 3ème cas lorsque l'utilisateur aura la possibilité de lire le fichier mais ne sera pas autorisé à lire les attributs du fichier?
Quête du
6

Vous pouvez également le faire bool b = std::ifstream('filename').good();. Sans les instructions de branchement (comme si), il doit fonctionner plus rapidement car il doit être appelé des milliers de fois.

parv
la source
Comme le montre la réponse acceptée, ce n'est pas vrai. Tout compilateur sérieux émettra probablement le même code, que vous le mettiez ou non. Par rapport aux variantes plain-C, la construction de l'objet ifstream (même s'il est sur la pile) entraîne une surcharge supplémentaire.
minexew
5

Si vous devez faire la distinction entre un fichier et un répertoire, tenez compte des éléments suivants, qui utilisent tous deux stat, l'outil standard le plus rapide, comme le montre 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;
}
user3902302
la source
4

J'ai besoin d'une fonction rapide qui peut vérifier si un fichier existe ou non et la réponse de PherricOxide est presque ce dont j'ai besoin, sauf qu'elle ne compare pas les performances de boost :: filesystem :: exist et les fonctions ouvertes. D'après les résultats de référence, nous pouvons facilement constater que:

  • L'utilisation de la fonction stat est le moyen le plus rapide de vérifier si un fichier existe. Notez que mes résultats sont cohérents avec ceux de la réponse de PherricOxide.

  • La performance de la fonction boost :: filesystem :: exist est très proche de celle de la fonction stat et elle est également portable. Je recommanderais cette solution si les bibliothèques boost sont accessibles à partir de votre code.

Résultats de référence obtenus avec le noyau Linux 4.17.0 et 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

Voici mon code de référence:

#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
la source
4

Vous pouvez utiliser std::ifstream, funcion comme is_open, failpar exemple comme ci - dessous le code (le Cout exist fichier « ouvert » moyens ou non):

entrez la description de l'image ici

entrez la description de l'image ici

cité à partir de cette réponse

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

Rest votre séquence de choses semblables à un chemin, et exists()vient du futur std ou boost actuel. Si vous lancez le vôtre, restez simple,

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

La solution ramifiée n'est pas absolument terrible et ne gobera pas les descripteurs de fichiers,

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
la source
PathFileExistsest limité à MAX_PATH(260) caractères; GetFileAttributesn'a pas cette limitation.
Felix Dombek
GetFileAttributesest également limité à MAX_PATH. Les documents décrivent une solution de contournement si vous utilisez des chemins absolus, unicode et ajoutez une chaîne de préfixe spéciale au nom du chemin. Je pense que nous sommes de toute façon tangents avec les réponses spécifiques à Windows.
John
1
GetFileAttributesWn'a pas de limitation.
Laurie Stearn
1

En 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
la source
5
C'est moins instructif que la réponse donnée par Vincent 4 ans plus tôt.
Jim Balter
2
En C ++ 17, le système de fichiers n'est plus expérimental
Quest
0

En utilisant MFC, il est possible avec les éléments suivants

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

Où se FileNametrouve une chaîne représentant le fichier dont vous vérifiez l'existence

Andy Bantly
la source
0

il n'y a qu'une seule façon plus rapide de vérifier si le fichier existe et si vous avez la permission de le lire, la façon d'utiliser le langage C est plus rapide et peut être utilisée aussi dans n'importe quelle version en C ++

solution : en C il y a une bibliothèque errno.h qui a une variable entière (globale) externe appelée errno qui contient un nombre qui peut être utilisé pour reconnaître le type d'erreur

    #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
la source
-4

Bien qu'il existe plusieurs façons de le faire, la solution la plus efficace à votre problème serait probablement d'utiliser l'une des méthodes prédéfinies du fstream telles que good () . Avec cette méthode, vous pouvez vérifier si le fichier que vous avez spécifié existe ou non.

fstream file("file_name.txt");

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

J'espère que vous trouvez ça utile.

miksiii
la source
4
Ce code créera le fichier s'il n'existe pas, donc le résultat sera toujours vrai. Vous devez soit utiliser ifstream, soit définir correctement le paramètre openmode.
Lubo Antonov