La manière la plus rapide de vérifier si un fichier existe en utilisant standard C++/C++11/c?
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 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 */
}
17 réponses
Eh bien, j'ai créé un programme d'essai qui a exécuté chacune de ces méthodes 100 000 fois, la moitié sur des fichiers qui existaient et l'autre moitié 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);
}
les Résultats pour le total des temps de courir les 100 000 appels en moyenne plus de 5 pistes,
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
étant votre meilleur pari si vous pour une raison quelconque refusez D'utiliser les fonctions POSIX.
j'utilise ce morceau de code, il fonctionne 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();
}
cela dépend de l'endroit où résident les fichiers. Par exemple, si ils sont tous censés être 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 sur la table de hachage. Ce pourrait être plus rapide sur certains systèmes que la vérification de chaque fichier individuellement. La manière la plus rapide de vérifier chaque fichier individuellement dépend de votre système ... si vous écrivez ANSI C, la manière la plus rapide est fopen
parce que c'est la seule façon (un fichier peut-être existe mais pas ouvert, mais vous voulez probablement vraiment ouvert si vous avez besoin de"faire quelque chose sur elle"). C++, POSIX, Windows offrent toutes des options supplémentaires.
Pendant que j'y suis, permettez-moi de souligner quelques problèmes avec votre question. Vous dites que vous voulez la manière la plus rapide, et que vous avez des milliers de fichiers, mais alors vous demandez le code pour une fonction pour tester un seul fichier (et cette fonction n'est valide qu'en C++, pas en C). Ceci contredit vos exigences en faisant hypothèse sur la solution ... un cas de XY problème . Vous dites aussi "en standard C++11(ou)C++(ou)c" ... qui sont tous différents, et c'est également incompatible avec vos besoins de vitesse ... la solution la plus rapide consisterait à adapter le code au système cible. L'incohérence de 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 qui ne sont pas standard C ou C++.
pour ceux qui aiment boost:
boost::filesystem::exists(fileName)
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 multiplateformes pour Windows et les systèmes conformes à POSIX.
identique à celle suggérée par le PherricOxide mais en C
#include <sys/stat.h>
int exist(const char *name)
{
struct stat buffer;
return (stat (name, &buffer) == 0);
}
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.
}
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;
}
vous pouvez aussi faire bool b = std::ifstream('filename').good();
. Sans les instructions de branche (comme si) il doit effectuer plus rapidement car il doit être appelé des milliers de fois.
si vous avez besoin de distinguer entre un fichier et un répertoire, considérer ce qui suit qui utilisent tous les deux stat qui l'outil standard le plus rapide comme démontré par le 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;
}
all_of (begin(R), end(R), [](auto&p){ exists(p); })
où R
est votre séquence de choses comme le chemin, et exists()
est du futur std ou boost actuel. Si vous roulez votre propre, gardez-le simple,
bool exists (string const& p) { return ifstream{p}; }
La ramifiés solution n'est pas absolument terrible et ne pas gober 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
}
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'il ne compare pas les performances de boost::filesystem::exists et ouvrir des fonctions. D'après les résultats de l'analyse comparative, nous pouvons facilement voir 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 compatibles avec ceux de la réponse de PherricOxide.
-
la performance de boost::filesystem::exists fonction est très proche de celle de stat fonction et il est également portable. Je recommande cette solution si boost libraries est accessible à 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
ci-dessous est 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();
en utilisant MFC il est possible avec le suivant
CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);
où FileName
est une chaîne représentant le fichier que vous vérifiez pour l'existence
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);
}
Bien qu'il existe plusieurs façons pour ce faire, la solution la plus efficace à votre problème serait probablement d'utiliser l'un des fstream prédéfinis méthode comme bonne() . Avec cette méthode, vous pouvez vérifier si le fichier que vous avez spécifié existe ou pas.
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 trouverez cela utile.