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

454

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

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Винсент
источник
2
boost::filesystemкажется использовать stat(). (Исходя из документации.) Я не думаю, что вы можете делать намного быстрее для вызовов FS. Способ сделать то, что вы делаете быстро, это «избегать просмотра тысяч файлов».
миллимус
16
Вопрос TOCTOU : откуда вы знаете, что файл не связан между вашей проверкой существует () и вашим "что-то делать с этим" ?
Пилкроу
7
@pilcrow Хорошая мысль, но есть довольно широкий спектр приложений, которым не нужно много правильности. Например, git pushвероятно, не стоит беспокоиться, чтобы убедиться, что вы не касаетесь рабочего дерева после начальной грязной проверки.
миллимус
9
«Я не могу представить себе реализацию C / C ++, в которой ее бы не было» - Windows не предоставляет среду POSIX.
Джим Балтер

Ответы:

778

Ну, я собрал тестовую программу, которая запускала каждый из этих методов 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.

PherricOxide
источник
31
Ни один из описанных выше методов не проверяет существование, а скорее доступность. Я не знаю ни одного стандартного C или C ++ способа проверить существование.
II
10
stat()кажется, чтобы проверить на существование.
el.pescado
105
Любой, кто использует это, должен запомнить #include <sys / stat.h>, в противном случае он попытается использовать неправильную статистику.
Катяни
23
Я предполагаю, что для метода ifstream вам не нужно, так f.close()как f выходит из области видимости в конце функции. Так return f.good()может заменить ifблок?
ilent2
11
Вы также можете использовать / test en.cppreference.com/w/cpp/experimental/fs/exists из следующего стандарта
zahir
154

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

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

и начиная с C ++ 17, только:

std::filesystem::exists("helloworld.txt");
Винсент
источник
5
уже доступно в Boost.Filesystem
TemplateRex
1
В MS Visual Studio 2013 эта функция доступна в разделеstd::tr2::sys::exists("helloworld.txt");
Константин
3
Я на самом деле надеюсь, что этого не произойдет std::exists, это было бы довольно странно (подумайте: существует в контейнере STL, как набор).
einpoklum
3
Также в Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Орвеллофил
1
Не забудьте#include <experimental/filesystem>
Мохаммед Noureldin
114

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

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
harryngh
источник
8
Тем не менее, он может потерпеть неудачу, если файл был заблокирован другой программой или если нет доступа к файлу.
Jet
2
вам нужно закрыть поток?
Mo0gles
29
@ Mo0gles: ifstreamдеструктор будет вызван при выходе is_file_existи закроет поток.
Исаак
2
Начиная с C ++ 11 вы можете сделать это в одну строку, используя оператор bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen
6
@Orwellophilereturn std::ifstream(fileName);
emlai
27

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

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

Джим Балтер
источник
25

Для тех, кто любит повышение:

 boost::filesystem::exists(fileName)
anhoppe
источник
5
Повышение обычно очень медленное.
Серж Рогач
4
Для большинства приложений проверка наличия файла не является критичной для производительности
anhoppe
29
Не все аспекты высокопроизводительного приложения требуют оптимизации. Например, чтение командной строки или файла конфигурации может быть сложным и может не требовать скорости, хотя само приложение может требовать преимуществ производительности C ++. Избежание Boost в таких случаях представляет собой переосмысление колеса, занимающее первое место в списке анти-паттернов.
Евоскуйл
5
@SergeRogatch boost :: filesystem :: Существует не очень медленно. Смотрите мои результаты теста для получения подробной информации.
Hungptit
3
«Повышение обычно происходит очень медленно» - это неверно, и даже неясно, в чем заключается суть заявления ... Повышение содержит множество пакетов от разных авторов, но проверено на высокое качество. «Для большинства приложений проверка наличия файла не является критичной для производительности», - ОП специально запрашивал скорость из-за проверки очень большого количества файлов. «Если производительность не критична, то также нет смысла использовать C ++» - еще один ошибочный комментарий (и не по теме). Большая часть программного обеспечения написана в магазинах и является частью системы, которая требует выбора языка.
Джим Балтер
23

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

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

Виктор Лиер
источник
Это работает на Mac? У меня нет Mac, но я ожидаю, что Mac сможет unistd.hтакже включать . Может быть, первым #ifdefдолжен быть конкретный windows?
Матф
5
Mac OSX является POSIX-совместимым.
Schaiba
20

То же, что предложено 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-эквивалент.
Рамон Ла Пьетра
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
источник
19
Если вы действительно собираетесь это сделать, просто «верните (bool) файл» вместо использования ветки if / else.
Ник Халдиманн
Не забудьте закрыть файл в случае истинного случая. Это тип утечки памяти, если вы оставляете файл открытым в течение всего времени выполнения программы, не говоря уже о том, что он может заблокировать ваш файл, так что вы не сможете прочитать его, узнав, что он существует .. add: file.close () на второе место.
Билл Мур
2
если подумать, может быть, вам не нужно явно закрывать его ... Я забыл, что если stream является RAII (Resource Acquisition Is Initialization) ... и будет очищаться, когда выходит из области действия деструктора ... что могу ли я сказать ... мне промывают мозги языки сборщика мусора в эти дни ...
Билл Мур
@BillMoore Ваш второй комментарий правильный; Многие другие комментарии на этой странице отметили, close()что нет необходимости.
Кит М
Это проверяет доступность, а не существование. Например, если файл существует, но доступ к нему невозможен из-за прав доступа, он вернет false, ошибочно утверждая, что файл не существует.
SasQ
7

Еще 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;
}
ravin.wang
источник
OpenFile является только ANSI и ограничен 128 символами .
Дэвид Бремнер
5
GetFileAttributesВерсия в основном канонический способ сделать это в Windows.
Феликс Домбек
Я знаю, что это старый, но что произойдет в 3-м случае, когда пользователь имеет возможность читать файл, но не имеет права читать атрибуты файла?
Задание
6

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

parv
источник
Как показывает принятый ответ, это не соответствует действительности. Любой серьезный компилятор, вероятно, выдаст один и тот же код, независимо от того, вставили вы в if или нет. По сравнению с вариантами plain-C создание объекта ifstream (даже если он находится в стеке) требует дополнительных затрат.
Minexew
6

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

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

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

  • Производительность функции boost :: filesystem :: exist очень близка к производительности функции 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();   
hungptit
источник
4

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

введите описание изображения здесь

введите описание изображения здесь

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

Jayhello
источник
3
all_of (begin(R), end(R), [](auto&p){ exists(p); })

где Rваша последовательность вещей, похожих на пути, и exists()от будущего стандартного или текущего повышения. Если вы катите свои собственные, будьте проще,

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
}
Джон
источник
PathFileExistsограничено MAX_PATH(260) символами; GetFileAttributesне имеет этого ограничения.
Феликс Домбек
GetFileAttributesтакже ограничен MAX_PATH. Документы описывают обходной путь, если вы используете абсолютные пути, юникод и добавляете специальную строку префикса к имени пути. Я думаю, что мы все равно не согласны с ответами, специфичными для Windows.
Джон
1
GetFileAttributesWне имеет ограничений.
Лори Стерн
1

В 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);
}
Абхиджит Кандалкар
источник
5
Это менее информативно, чем ответ, данный Винсентом 4 года назад.
Джим Балтер
2
В C ++ 17 файловая система больше не является экспериментальной
Quest
0

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

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

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

Энди Бэнтли
источник
0

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

Решение : в C есть библиотека errno.h, которая имеет внешнюю (глобальную) целочисленную переменную с именем errno, которая содержит число, которое можно использовать для распознавания типа ошибки

    #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;
    }
Имад Дираа
источник
-4

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

fstream file("file_name.txt");

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

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

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