Найшвидший спосіб перевірити, чи існує файл, використовуючи стандартний C ++ / C ++ 11 / C?


453

Я хотів би знайти найшвидший спосіб перевірити, чи існує файл у стандартних C ++ 11, C ++ або C. У мене є тисячі файлів, і перш ніж робити щось на них, мені потрібно перевірити, чи всі вони існують. Що можна написати замість /* SOMETHING */наступної функції?

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

2
boost::filesystemздається, використовує stat(). (Припускаючи документацію.) Я не думаю, що ви можете зробити набагато швидше для FS-дзвінків. Швидкий спосіб зробити те, що ви робите, - це "уникати перегляду тисяч файлів".
мільмозу

16
Питання TOCTOU : як ви знаєте, що файл не пов’язаний між вашою чеком існує () та вашим "чимось на цьому" ?
pilcrow

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-функцій.


31
Жоден із наведених вище способів не перевіряє наявність, а швидше доступність. Я не знаю, як існує єдиний стандартний C або C ++ спосіб перевірити наявність.
IIнеочікуваний

10
stat()здається, перевіряє наявність.
el.pescado

105
Кожен, хто використовує це, повинен пам’ятати про #include <sys / stat.h>, інакше він намагається використовувати неправильний stat.
Катяні

23
Я думаю, що для методу ifstream вам не потрібно, f.close()оскільки f виходить із сфери дії в кінці функції. Так return f.good()міг би замінити ifблок?
ilent2

11
Ви також можете використовувати / test en.cppreference.com/w/cpp/experimental/fs/exists з майбутнього стандарту
zahir

153

Зауваження: в 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"); }
Orwellophile

1
Не забувайте#include <experimental/filesystem>
Мохаммед Нурелдін

112

Я використовую цей фрагмент коду, він працює добре зі мною до цих пір. Це не використовує багато фантазійних функцій C ++:

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

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)

5
Збільшення зазвичай надзвичайно повільне.
Серж Рогач

4
Для більшості застосунків файл перевірка не є критичною для продуктивності
anhoppe

29
Не всі аспекти програми з високою продуктивністю вимагають оптимізації. Наприклад, читання командного рядка або конфігураційного файлу може бути складним і може не вимагати швидкості, хоча сама програма може вимагати переваг продуктивності C ++. Уникнення прискорення в таких випадках є переосмисленням колеса, яке входить до списку анти-зразків.
evoskuil

5
@SergeRogatch boost :: файлова система :: існує не надто повільно. Детальну інформацію див. У моїх результатах.
голодував

3
"Збільшення зазвичай надзвичайно повільне" - це помилково, і навіть не ясно, яка сфера претензії ... Boost містить багато пакунків різних авторів, але перевіряється на високу якість. "Для більшості застосунків перевірка файлу не є критичною для продуктивності" - ОП спеціально просила швидкість через перевірку дуже великої кількості файлів. "Якщо продуктивність не є критичною, тоді також немає сенсу використовувати 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повинні бути саме вікна?
матч

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 ++. Я не знаю С ++, тому я розмістив еквівалент С.
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.
}

19
Якщо ви дійсно збираєтесь це робити, просто "поверніть (bool) файл", а не використовуйте гілку if / else.
Нік Халдіманн

Не забудьте закрити файл у випадку справжнього випадку. Це тип витоку пам’яті, якщо ви залишите файл відкритим протягом усього часу виконання програми, не кажучи вже про нього, він може заблокувати ваш файл, щоб ви не могли його прочитати, знаючи, що він існує .. add: file.close () до другого.
Білл Мур

2
по-друге думав, може, вам не потрібно явно закривати це ... Я забув, що ifstream - це RAII (Придбання ресурсів - це ініціалізація) ... і очистить себе, коли він вийде за межі від деструктора ... що чи можу я сказати ... Мене сьогодні промивають мозку мовою збирачів сміття ...
Білл Мур

@BillMoore Ваш другий коментар правильний; багато інших коментарів на цій сторінці зазначили close(), що не потрібно.
Кіт М

Це перевіряє доступність, а не існування. Наприклад, якщо файл існує, але до нього не можна отримати доступ через права доступу, він поверне помилкове, помилково стверджуючи, що файл не існує.
SasQ

7

Ще 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;
}


5
GetFileAttributesВерсія в основному канонічний спосіб зробити це в Windows.
Фелікс Домбек

Я знаю, що це старе, але що буде в 3-му випадку, коли користувач має можливість читати файл, але йому не дозволяється читати атрибути файлу?
Квест

6

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


Як показує прийнята відповідь, це неправда. Будь-який серйозний компілятор, ймовірно , випускає той же код , поставити ви в разі чи ні. Порівняно з варіантами звичайного 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;
}

4

Мені потрібна швидка функція, яка може перевірити, чи існує файл чи ні, і відповідь PherricOxide - це майже те, що мені потрібно, за винятком того, що вона не порівнює продуктивність boost :: fileystem :: існує та відкривати функції. З результатів еталону ми легко бачимо, що:

  • Використання функції stat - це найшвидший спосіб перевірити наявність файлу. Зауважте, що мої результати відповідають результатам відповіді PherricOxide.

  • Продуктивність функції boost :: fileystem :: існує дуже близька до функції 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();   

4

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

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

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

цитується з цієї відповіді


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
}

PathFileExistsобмежено MAX_PATH(260) символами; GetFileAttributesне має цього обмеження.
Фелікс Домбек

GetFileAttributesобмежено також MAX_PATH. Документи описують обхідне рішення, якщо ви використовуєте абсолютні шляхи, unicode та додаєте спеціальну рядок префікса до імені шляху. Я думаю, що ми все одно перебуваємо в дотичній ситуації зі специфічними для 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 файлова система вже не експериментальна
Квест

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

Я сподіваюся, що Ви вважаєте це корисним.


4
Цей код створить файл, якщо його не існує, тому результат завжди буде вірним. Вам потрібно або використовувати ifstream, або правильно встановити параметр openmode.
Любо Антонов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.