¿La forma más rápida de verificar si existe un archivo usando C ++ / C ++ 11 / C estándar?

453

Me gustaría encontrar la forma más rápida de verificar si un archivo existe en C ++ 11, C ++ o C. estándar. Tengo miles de archivos y antes de hacer algo en ellos necesito verificar si todos existen. ¿Qué puedo escribir en lugar de /* SOMETHING */en la siguiente función?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Vincent
fuente
2
boost::filesystemParece que usa stat(). (Suponiendo de la documentación.) No creo que pueda hacer mucho más rápido para las llamadas FS. La forma de hacer lo que está haciendo rápido es "evitar mirar miles de archivos".
millimoose
16
Pregunta de TOCTOU : ¿cómo sabe que el archivo no está desvinculado entre su cheque exist () y su "hacer algo al respecto" ?
Pilcrow
77
@pilcrow Buen punto, pero hay una gama bastante amplia de aplicaciones que no necesitan tanta corrección. Por ejemplo, git pushprobablemente no se moleste en asegurarse de que no esté tocando el árbol de trabajo después de la verificación sucia inicial.
millimoose
99
"No puedo pensar en una implementación de C / C ++ que no lo tenga": Windows no proporciona un entorno POSIX.
Jim Balter

Respuestas:

778

Bueno, elaboré un programa de prueba que ejecutaba cada uno de estos métodos 100,000 veces, la mitad en archivos que existían y la otra mitad en archivos que no existían.

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

Los resultados del tiempo total para ejecutar las 100,000 llamadas promediaron más de 5 ejecuciones,

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()función proporcionó el mejor rendimiento en mi sistema (Linux, compilado con g++), siendo una fopenllamada estándar su mejor opción si por alguna razón se niega a usar las funciones POSIX.

Óxido Pherric
fuente
31
Ninguno de los métodos anteriores verifica la existencia, sino la accesibilidad. Sin embargo, no conozco una sola forma estándar de C o C ++ para verificar la existencia.
Inspeccionable el
10
stat()Parece comprobar la existencia.
el.pescado
105
Cualquiera que use esto debe recordar #incluir <sys / stat.h> de lo contrario, intenta usar la estadística incorrecta.
Katianie
23
Me imagino que para el método ifstream, no es necesario f.close()ya que f sale del alcance al final de la función. Entonces return f.good(), ¿ podría reemplazar el ifbloque?
ilent2
11
También puede usar / test en.cppreference.com/w/cpp/experimental/fs/exists del próximo estándar
zahir
153

Observación: en C ++ 14 y tan pronto como el sistema de archivos TS esté terminado y adoptado, la solución será usar:

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

y desde C ++ 17, solo:

std::filesystem::exists("helloworld.txt");
Vincent
fuente
55
ya disponible en Boost.Filesystem
TemplateRex
1
En MS Visual Studio 2013 esta función está disponible enstd::tr2::sys::exists("helloworld.txt");
Constantin
3
De hecho, espero que no lo sea std::exists, eso sería bastante confuso (piense: existe en un contenedor STL como un conjunto).
einpoklum
3
También en Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile
1
No te olvides#include <experimental/filesystem>
Mohammed Noureldin
112

Utilizo este código, hasta ahora funciona bien conmigo. Esto no utiliza muchas características sofisticadas de C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
harryngh
fuente
8
Sin embargo, puede fallar si otro programa bloqueó el archivo o si no hay acceso al archivo.
Jet
2
¿Necesitas cerrar la transmisión?
mueve el
29
@ Mo0gles: ifstreamse llamará al destructor al salir is_file_existy cerrará la transmisión.
Isaac
2
A partir de C ++ 11, puede hacerlo en una línea usando el operador bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen
66
@Orwellophilereturn std::ifstream(fileName);
emlai
27

Depende de dónde residan los archivos. Por ejemplo, si se supone que todos están en el mismo directorio, puede leer todas las entradas del directorio en una tabla hash y luego verificar todos los nombres en la tabla hash. Esto puede ser más rápido en algunos sistemas que verificar cada archivo individualmente. La forma más rápida de verificar cada archivo individualmente depende de su sistema ... si está escribiendo ANSI C, la forma más rápida esfopen porque es la única forma (un archivo puede existir pero no puede abrirse, pero probablemente realmente quiera abrirlo si necesita "hacer algo al respecto"). C ++, POSIX, Windows ofrecen opciones adicionales.

Mientras estoy en ello, permítanme señalar algunos problemas con su pregunta. Dices que quieres la forma más rápida y que tienes miles de archivos, pero luego pides el código de una función para probar un solo archivo (y esa función solo es válida en C ++, no en C). Esto contradice sus requisitos al asumir que la solución ... es un caso del problema XY . También dice "en c ++ 11 estándar (o) c ++ (o) c" ... que son todos diferentes, y esto también es inconsistente con su requisito de velocidad ... la solución más rápida implicaría adaptar el código a objetivo del sistema. La inconsistencia en la pregunta se destaca por el hecho de que aceptó una respuesta que brinda soluciones que dependen del sistema y no son C o C ++ estándar.

Jim Balter
fuente
25

Para aquellos que les gusta el impulso:

 boost::filesystem::exists(fileName)
anhoppe
fuente
55
El refuerzo suele ser extremadamente lento.
Serge Rogatch
44
Para la mayoría de las aplicaciones, la verificación de un archivo no es crítica para el rendimiento
anhoppe
29
No todos los aspectos de una aplicación de alto rendimiento requieren optimización. Por ejemplo, leer la línea de comandos o un archivo de configuración puede ser complejo y puede que no requiera velocidad, aunque la aplicación en sí puede requerir las ventajas de rendimiento de C ++. Evitar el refuerzo en tales casos constituye una reinvención de la rueda, que ocupa un lugar destacado en la lista antipatrón.
evoskuil
55
@SergeRogatch boost :: filesystem :: Existe no es extremadamente lento. Vea mis resultados de referencia para obtener información detallada.
Hungptit
3
"Boost suele ser extremadamente lento"; esto es falso, y ni siquiera está claro cuál es el alcance de la afirmación ... Boost contiene muchos paquetes de diferentes autores, pero se verifica su alta calidad. "Para la mayoría de las aplicaciones, la comprobación de un archivo no es crítica para el rendimiento": el OP solicitó específicamente la velocidad debido a la verificación de una gran cantidad de archivos. "Si el rendimiento no es crítico, entonces tampoco tiene sentido usar C ++" - otro comentario erróneo (y fuera de tema). La mayoría del software está escrito en tiendas y es parte de un sistema que exige la elección del idioma.
Jim Balter
23

Sin usar otras bibliotecas, me gusta usar el siguiente fragmento de código:

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

Esto funciona multiplataforma para Windows y sistemas compatibles con POSIX.

Viktor Liehr
fuente
¿Funciona esto en Mac? No tengo una Mac, pero esperaría que una Mac también pueda incluirla unistd.h. ¿Quizás el primero #ifdefdebería ser específico de Windows?
Mat
55
Mac OSX es compatible con POSIX.
schaiba
20

Igual a lo sugerido por PherricOxide pero en C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}
Ramon La Pietra
fuente
1
.c_str () es una función de C ++. No sé C ++, así que publiqué un equivalente en 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
fuente
19
Si realmente va a hacer eso, simplemente "devuelva (bool) file" en lugar de usar una rama if / else.
Nik Haldimann
No olvide cerrar el archivo en el caso del caso verdadero. Ese es un tipo de pérdida de memoria si deja el archivo abierto durante todo el tiempo de ejecución del programa, sin mencionar que puede bloquear su archivo para que no pueda leerlo después de saber que existe ... add: file.close () a la segunda más.
Bill Moore
2
Pensándolo bien, tal vez no necesite cerrarlo explícitamente ... Olvidé que ifstream es un RAII (La adquisición de recursos es la inicialización) ... y se limpiará a medida que salga del alcance del destructor ... qué ¿puedo decir ... me bañan el cerebro con los idiomas de los recolectores de basura en estos días ...
Bill Moore
@BillMoore Su segundo comentario es correcto; muchos otros comentarios en esta página han señalado close()que no es necesario.
Keith M
Esto verifica la accesibilidad, no la existencia. Por ejemplo, si el archivo existe, pero no se puede acceder debido a los derechos de acceso, devolverá falso, alegando erróneamente que el archivo no existe.
SasQ
7

Otras 3 opciones en 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
fuente
OpenFile es solo ANSI y está limitado a 128 caracteres .
David Bremner
55
La GetFileAttributesversión es básicamente la forma canónica de hacerlo en Windows.
Felix Dombek
Sé que esto es antiguo, pero ¿qué sucederá en el tercer caso cuando el usuario tenga la capacidad de leer el archivo pero no se le permita leer los atributos del archivo?
Búsqueda
6

También puedes hacer bool b = std::ifstream('filename').good();. Sin las instrucciones de bifurcación (como si) debe funcionar más rápido, ya que debe llamarse miles de veces.

parv
fuente
Como muestra la respuesta aceptada, esto no es cierto. Cualquier compilador seria probablemente emitirá el mismo código si se pone en el caso o no. En comparación con las variantes de C simple, la construcción del objeto ifstream (incluso si está en la pila) conlleva una sobrecarga adicional.
minexew
5

Si necesita distinguir entre un archivo y un directorio, considere lo siguiente, que usan stat, que es la herramienta estándar más rápida como lo demuestra 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;
}
usuario3902302
fuente
4

Necesito una función rápida que pueda verificar si un archivo existe o no, y la respuesta de PherricOxide es casi lo que necesito, excepto que no compara el rendimiento de boost :: filesystem :: Existe y abre funciones. De los resultados de referencia podemos ver fácilmente que:

  • Usar la función estadística es la forma más rápida de verificar si existe un archivo. Tenga en cuenta que mis resultados son consistentes con los de la respuesta de PherricOxide.

  • El rendimiento de la función boost :: filesystem :: exist es muy similar al de la función stat y también es portátil. Recomendaría esta solución si se puede acceder a las bibliotecas de impulso desde su código.

Resultados de referencia obtenidos con el kernel 4.17.0 de Linux y 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

A continuación se muestra mi código de referencia:

#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
fuente
4

Puede usar std::ifstream, como is_open, failpor ejemplo, como se muestra a continuación (el código "abierto" significa que el archivo existe o no):

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

citado de esta respuesta

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

¿Dónde Restá su secuencia de cosas similares a la ruta, y exists()es del futuro estándar o impulso actual? Si rueda el suyo, que sea sencillo,

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

La solución ramificada no es absolutamente terrible y no engullirá los descriptores de archivo,

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
}
Juan
fuente
PathFileExistsestá limitado a MAX_PATH(260) caracteres; GetFileAttributesNo tiene esta limitación.
Felix Dombek
GetFileAttributestambién está limitado a MAX_PATH. Los documentos describen una solución alternativa si utiliza rutas absolutas, unicode y antepone una cadena de prefijo especial al nombre de la ruta. Creo que estamos en una tangente con las respuestas específicas de Windows de todos modos.
John
1
GetFileAttributesWNo tiene la limitación.
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
fuente
55
Esto es menos informativo que la respuesta dada por Vincent 4 años antes.
Jim Balter
2
En C ++ 17, el sistema de archivos ya no es experimental
Búsqueda del
0

Usando MFC es posible con lo siguiente

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

¿Dónde FileNameestá una cadena que representa el archivo que está buscando para la existencia?

Andy Bantly
fuente
0

solo hay una forma más rápida de verificar si el archivo existe y si tiene permiso para leerlo, la forma en que usa el lenguaje C es más rápido y se puede usar en cualquier versión en C ++

solución : en C hay una biblioteca errno.h que tiene una variable entera externa (global) llamada errno que contiene un número que puede usarse para reconocer el tipo de error

    #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
fuente
-4

Aunque hay varias maneras de hacer esto, la solución más eficiente para su problema probablemente sería usar uno de los métodos predefinidos de fstream como good () . Con este método puede verificar si el archivo que ha especificado existe o no.

fstream file("file_name.txt");

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

Espero que encuentres esto útil.

miksiii
fuente
44
Este código creará el archivo si no existe, por lo que el resultado siempre será verdadero. Debe usar ifstream o establecer el parámetro de modo abierto correctamente.
Lubo Antonov