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

324

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

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
  • 1
    Не уверен, как проверка на существование влияет на скорость процессора и скорости диска, но я держу пари, что это медленный диск.
  • 2
    boost::filesystem кажется, использует stat() . (Исходя из документации.) Я не думаю, что вы можете делать намного быстрее для вызовов FS. Способ сделать то, что вы делаете быстро, - это «не смотреть на тысячи файлов».
Показать ещё 10 комментариев
Теги:
file
stream

17 ответов

575
Лучший ответ

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

#include <sys/stat.h>
#include <unistd.h>
#include <string>

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.

  • 24
    Ни один из описанных выше методов не проверяет существование, а скорее доступность. Я не знаю ни одного стандартного C или C ++ способа проверки на существование.
  • 5
    stat() кажется, проверяет существование.
Показать ещё 12 комментариев
86

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

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
  • 6
    Тем не менее, это может произойти сбой, если файл был заблокирован другой программой или если нет доступа к файлу.
  • 2
    вам нужно закрыть поток?
Показать ещё 4 комментария
71

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

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

и поскольку С++ 17, только:

std::filesystem::exists("helloworld.txt");
  • 4
    уже доступно в Boost.Filesystem
  • 1
    В MS Visual Studio 2013 эта функция доступна в std::tr2::sys::exists("helloworld.txt");
Показать ещё 4 комментария
23

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

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

21

Для тех, кому нравится boost:

 boost::filesystem::exists(fileName)
  • 4
    Повышение обычно очень медленное.
  • 4
    Для большинства приложений проверка наличия файла не является критичной для производительности
Показать ещё 5 комментариев
16

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

#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-совместимых систем.

  • 0
    Это работает на Mac? У меня нет Mac, но я ожидаю, что Mac сможет также включать unistd.h . Может быть, первый #ifdef должен быть специфичен для Windows?
  • 2
    Mac OSX является POSIX-совместимым.
14

То же, что предложено PherricOxide, но в C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}
  • 1
    .c_str () является функцией C ++. Я не знаю C ++, поэтому я разместил эквивалент C.
9
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.
}
  • 14
    Если вы действительно собираетесь это сделать, просто «верните (bool) файл» вместо использования ветки if / else.
  • 0
    @nhaldimann, на самом деле приведение к bool не нужно
Показать ещё 4 комментария
6

Еще 3 варианта под окнами:

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;
}
  • 0
    OpenFile является только ANSI и ограничен 128 символами .
  • 5
    Версия GetFileAttributes - это в основном канонический способ сделать это в Windows.
Показать ещё 1 комментарий
4

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

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

  • 0
    Как показывает принятый ответ, это не соответствует действительности. Любой серьезный компилятор, вероятно, выдаст один и тот же код, независимо от того, вставили вы в if или нет. По сравнению с вариантами plain-C создание объекта ifstream (даже если он находится в стеке) требует дополнительных затрат.
  • 0
    Код будет менее читабельным, хотя: q
3
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
}
  • 0
    PathFileExists ограничен MAX_PATH (260) символов; GetFileAttributes не имеет этого ограничения.
  • 0
    GetFileAttributes ограничен MAX_PATH. Документы описывают обходной путь, если вы используете абсолютные пути, юникод и добавляете специальную строку префикса к имени пути. Я думаю, что мы все равно не согласны с ответами, специфичными для Windows.
Показать ещё 1 комментарий
1

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

Изображение 1254

Изображение 1255

цитируется из этого ответа

0

В С++ 17:

#include <experimental/filesystem>

bool is_file_exist(std::string& str) {   
    namespace fs = std::experimental::filesystem;
    fs::path p(str);
    return fs::exists(p);
}
  • 2
    Это менее информативно, чем ответ, данный Винсентом 4 года назад.
  • 0
    В C ++ 17 файловая система больше не является экспериментальной
0

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

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

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

Результаты тестов, полученные с ядром 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();   
0

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

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

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

-2

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

fstream file("file_name.txt");

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

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

  • 4
    Этот код создаст файл, если он не существует, поэтому результат всегда будет верным. Вам нужно либо использовать ifstream, либо правильно установить параметр openmode.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню