Самый быстрый способ проверить, существует ли файл с использованием стандартного C++/C++11 / C?

Я хотел бы найти самый быстрый способ проверить, существует ли файл в стандартном c/" class="blnk">C++11, C++ или C. У меня есть тысячи файлов, и прежде чем что-то делать на них, мне нужно проверить, все ли они существуют. Что я могу написать вместо /* SOMETHING */ в следующей функции?

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

17 ответов


Ну, я собрал тестовую программу, которая запускала каждый из этих методов 100 000 раз, наполовину на файлах, которые существовали, и наполовину на файлах, которые не существовали.

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

результаты за общее время выполнения 100 000 вызовов в среднем за 5 запусков,

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

на stat() функция обеспечила лучшую производительность в моей системе (Linux, скомпилированный с g++), со стандартным fopen звоните лучше всего, если вы по какой-то причине отказываетесь использовать функции POSIX.


Я использую этот кусок кода, он работает со мной до сих пор. Это не использует много причудливых функций C++:

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

Примечание : В C++14, и как только файловая система TS будет закончено и принято, решение будет использовать:

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

и так как C++17, только:

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

Это зависит от того, где эти файлы находятся. Например, если все они должны находиться в одном каталоге, вы можете прочитать все записи каталога в хэш-таблицу, а затем проверить все имена в хэш-таблице. Это может быть быстрее в некоторых системах, чем проверять каждый файл по отдельности. Самый быстрый способ проверить каждый файл индивидуально зависит от вашей системы ... если вы пишете ANSI C, самый быстрый способ -fopen потому что это единственный способ (файл может существовать, но не открываем, но вы, вероятно, действительно хотите открыть, если вам нужно "что-то сделать"). C++, POSIX, Windows предлагают дополнительные опции.

пока я здесь, позвольте мне указать на некоторые проблемы с вашим вопросом. Вы говорите, что хотите самый быстрый способ и что у вас есть тысячи файлов, но затем вы просите код для функции для тестирования одного файла (и эта функция действительна только на C++, а не на C). Это противоречит вашим требованиям, делая предположение о решении ... случай проблема XY. Вы также говорите "в стандартном c++11 (или)C++(или) c" ... которые все разные, и это также несовместимо с вашим требованием к скорости ... самое быстрое решение - адаптация кода к целевой системе. Несоответствие в вопросе подчеркивается тем фактом, что вы приняли ответ, который дает решения, зависящие от системы и не являющиеся стандартными C или c++.


для тех, кто любит boost:

 boost::filesystem::exists(fileName)

без использования других библиотек мне нравится использовать следующий фрагмент кода:

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

это работает кросс-платформенный для Windows и POSIX-совместимых систем.


то же, что и в PherricOxide, но в 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 варианта под 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;
}

вы также можете сделать bool b = std::ifstream('filename').good();. Без инструкций филиала (например, if) он должен работать быстрее, так как его нужно вызывать тысячи раз.


Если вам нужно различать файл и каталог, рассмотрите следующее, что оба используют stat, который является самым быстрым стандартным инструментом, как показано 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); })

здесь R - это последовательность пути-как вещи, и exists() из будущего std или ток увеличить. Если вы катите свой собственный, держите его простым,

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

разветвленное решение не совсем ужасно, и оно не будет поглощать файловые дескрипторы,

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
}

мне нужна быстрая функция, которая может проверить, существует ли файл или нет, и ответ PherricOxide-это почти то, что мне нужно, за исключением того, что он не сравнивает производительность boost::filesystem::exists и open functions. Из результатов теста мы можем легко увидеть, что :

  • использование функции stat-самый быстрый способ проверить, существует ли файл. Обратите внимание, что мои результаты согласуются с ответом Феррикоксида.

  • производительность boost::filesystem:: exists функция очень близка к функции stat, и она также портативна. Я бы рекомендовал это решение, если библиотеки boost доступны из вашего кода.

результаты тестов, полученные с ядром Linux 4.17.0 и 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

Ниже приведен мой контрольный код:

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

можно использовать std::ifstream, funcion как is_open, fail, например, как показано ниже кода (cout "open" означает, что файл существует или нет):

enter image description here

enter image description here

цитируется по этой ответ


использование MFC возможно со следующим

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

здесь FileName - это строка, представляющая файл, который вы проверяете на существование


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

хотя есть несколько способов сделать это, наиболее эффективным решением вашей проблемы, вероятно, будет использование одного из предопределенных методов fstream, таких как хороший(). С помощью этого метода вы можете проверить, существует ли указанный файл или нет.

fstream file("file_name.txt");

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

Я надеюсь, вы найдете это полезным.