web-dev-qa-db-fra.com

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

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

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

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 existants 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 du temps total nécessaire pour exécuter les 100 000 appels en moyenne sur 5 analyses,

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 fonction stat() a fourni les meilleures performances sur mon système (Linux, compilé avec g++), avec un appel standard fopen comme votre meilleur choix si vous refusez d'utiliser les fonctions POSIX.

679
PherricOxide

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");
113
Vincent

J'utilise ce morceau de code, ça marche bien avec moi jusqu'à présent. Cela n'utilise pas beaucoup de fonctionnalités sophistiquées de C++:

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

Cela dépend de l'emplacement des fichiers. Par exemple, s'ils sont tous supposé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 comparer tous les noms par rapport à la table de hachage. Cette pourrait être plus rapide sur certains systèmes que de vérifier chaque fichier individuellement. Le moyen le plus rapide de vérifier chaque fichier individuellement dépend de votre système ... si vous écrivez en ANSI C, le plus rapide est fopen car c'est le seul moyen (un fichier peut exister mais ne peut pas être ouvert, mais vous vraiment envie d’être ouvrable si vous avez besoin de "faire quelque chose dessus"). C++, POSIX, Windows offrent tous des options supplémentaires.

Pendant que j'y suis, laissez-moi 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 ensuite vous demandez le code pour une fonction permettant de tester un seul fichier (et cette fonction est uniquement valide en C++, pas en C). Cela contredit vos exigences en faisant une hypothèse sur la solution ... un cas de le problème XY . Vous dites également "en standard c ++ 11 (ou) c ++ (ou) c" ... qui sont tous différents, ce qui est également incompatible avec vos impératifs de rapidité ... la solution la plus rapide impliquerait d'adapter le code à la système cible. Cette incohérence dans la question est mise en évidence par le fait que vous avez accepté une réponse qui donne des solutions dépendant du système et qui ne sont pas du standard C ou C++.

26
Jim Balter

Pour ceux qui aiment boost:

 boost::filesystem::exists(fileName)
22
anhoppe

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 compatibles Windows et POSIX.

19
Viktor Liehr

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);
}
17
Ramon La Pietra
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.
}
10
LOLOLOL

Un autre 3 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;
}
7
ravin.wang

Si vous devez faire la distinction entre un fichier et un répertoire, tenez compte des éléments suivants: ils utilisent tous deux stat qui est 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;
}
5
user3902302

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

4
parv

J'ai besoin d'une fonction rapide qui puisse 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 :: existe et des 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 correspondent à ceux de la réponse de PherricOxide.

  • La performance de la fonction boost :: filesystem :: existe 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 les noyaux 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

Ci-dessous 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();   
3
hungptit
all_of (begin(R), end(R), [](auto&p){ exists(p); })

R est votre séquence d'objets semblables à un chemin et exists() provient du futur boost standard ou actuel. Si vous faites le vôtre, restez simple,

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

La solution en branches n’est pas vraiment terrible et elle ne gobble 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
}
3
John

Vous pouvez utiliser std::ifstream, fonction comme is_open, fail, par exemple comme ci-dessous code (le cout "open" signifie que le fichier existe ou non):

enter image description here

enter image description here

cité de ceci réponse

3
Jayhello

En utilisant MFC, il est possible avec ce qui suit

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

FileName est une chaîne représentant le fichier que vous recherchez.

0
Andy Bantly