Najszybszy sposób sprawdzenia, czy plik istnieje przy użyciu standardowego C ++ / C ++ 11 / C?

453

Chciałbym znaleźć najszybszy sposób sprawdzenia, czy plik istnieje w standardowym C ++ 11, C ++ lub C. Mam tysiące plików i zanim coś z nimi zrobię, muszę sprawdzić, czy wszystkie istnieją. Co mogę napisać zamiast /* SOMETHING */w poniższej funkcji?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Vincent
źródło
2
boost::filesystemwydaje się używać stat(). (Zakładając, że z dokumentacji.) Nie sądzę, że możesz zrobić znacznie szybciej dla wywołań FS. Szybkim sposobem na zrobienie tego jest „unikanie patrzenia na tysiące plików”.
millimoose
16
Pytanie TOCTOU : skąd wiesz, że plik nie jest rozłączony między sprawdzeniem istnienia () a twoją „robieniem czegoś” ?
pilcrow
7
@pilcrow Dobra uwaga, ale istnieje dość szeroki zakres aplikacji, które nie wymagają tak dużej poprawności. Np. git pushPrawdopodobnie nie zadaje sobie trudu, aby upewnić się, że nie dotykasz działającego drzewa po początkowym brudnym sprawdzeniu.
millimoose
9
„Nie mogę wymyślić implementacji C / C ++, która by jej nie miała” - Windows nie zapewnia środowiska POSIX.
Jim Balter

Odpowiedzi:

778

Cóż, rzuciłem program testowy, który uruchomił każdą z tych metod 100 000 razy, w połowie na plikach, które istniały, a na połowie na plikach, które nie istniały.

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

Wyniki dla całkowitego czasu na wykonanie 100 000 połączeń uśrednionych dla 5 przebiegów,

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

Ta stat()funkcja zapewniała najlepszą wydajność w moim systemie (Linux, z kompilacją g++), przy czym standardowe fopenwywołanie jest najlepszym wyborem, jeśli z jakiegoś powodu odmawiasz korzystania z funkcji POSIX.

PherricOxide
źródło
31
Żadna z powyższych metod nie sprawdza istnienia, ale dostępność. Nie znam jednak żadnego standardowego sposobu sprawdzania istnienia w C lub C ++.
IInspectable
10
stat()wydaje się sprawdzać istnienie.
el.pescado,
105
Każdy, kto tego używa, musi pamiętać o #include <sys / stat.h>, w przeciwnym razie spróbuje użyć niewłaściwej statystyki.
Katianie
23
Wyobrażam sobie, że dla metody ifstream nie potrzebujesz, f.close()ponieważ f wychodzi poza zakres na końcu funkcji. Więc return f.good()może zastąpić ifblok?
ilent2
11
Możesz także użyć / test pl.cppreference.com/w/cpp/experimental/fs/exists z nadchodzącego standardu
zahir
153

Uwaga: w C ++ 14 i jak tylko system plików TS zostanie ukończony i przyjęty, rozwiązaniem będzie użycie:

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

a od C ++ 17 tylko:

std::filesystem::exists("helloworld.txt");
Vincent
źródło
5
już dostępny w Boost.Filesystem
TemplateRex
1
W MS Visual Studio 2013 ta funkcja jest dostępna podstd::tr2::sys::exists("helloworld.txt");
Constantin
3
Mam nadzieję, że tak nie będzie std::exists, byłoby to dość mylące (pomyśl: istnieje w kontenerze STL jak zestaw).
einpoklum
3
Również w Visual Studio 2015:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile
1
Nie zapomnij#include <experimental/filesystem>
Mohammed Noureldin
112

Używam tego fragmentu kodu, do tej pory działa ze mną dobrze. To nie używa wielu wymyślnych funkcji C ++:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
harryngh
źródło
8
Może się jednak nie powieść, jeśli plik został zablokowany przez inny program lub jeśli nie ma dostępu do pliku.
Jet
2
czy musisz zamknąć strumień?
Mo0gles,
29
@ Mo0gles: Destruktor ifstreamzostanie wywołany przy wychodzeniu is_file_existi zamknie strumień.
Izaak
2
Od wersji C ++ 11 można to zrobić w jednym wierszu, używając operatora bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen
6
@Orwellophilereturn std::ifstream(fileName);
emlai
27

To zależy od tego, gdzie znajdują się pliki. Na przykład, jeśli wszystkie mają znajdować się w tym samym katalogu, możesz odczytać wszystkie wpisy katalogu w tablicy skrótów, a następnie sprawdzić wszystkie nazwy w tabeli skrótów. W niektórych systemach może to być szybsze niż sprawdzanie każdego pliku osobno. Najszybszy sposób sprawdzenia każdego pliku osobno zależy od systemu ... jeśli piszesz ANSI C, najszybszym sposobem jest fopento, że jest to jedyny sposób (plik może istnieć, ale nie da się go otworzyć, ale prawdopodobnie naprawdę chcesz go otworzyć, jeśli trzeba „coś z tym zrobić”). C ++, POSIX, Windows oferują dodatkowe opcje.

Skoro już o tym mówię, pozwól mi wskazać pewne problemy z twoim pytaniem. Mówisz, że chcesz najszybszy sposób i że masz tysiące plików, ale potem pytasz o kod funkcji, by przetestować pojedynczy plik (i ta funkcja jest poprawna tylko w C ++, a nie w C). Jest to sprzeczne z Twoimi wymaganiami, zakładając, że rozwiązanie ... jest przypadkiem problemu XY . Mówisz także „w standardowym c ++ 11 (lub) c ++ (lub) c” ... które są różne, a to również jest niezgodne z twoim wymaganiem szybkości ... najszybsze rozwiązanie wymagałoby dostosowania kodu do Docelowy system. Niespójność w tym pytaniu podkreśla fakt, że zaakceptowałeś odpowiedź, która daje rozwiązania zależne od systemu i nie będące standardowym C lub C ++.

Jim Balter
źródło
25

Dla tych, którzy lubią boost:

 boost::filesystem::exists(fileName)
anhoppe
źródło
5
Zwiększenie jest zwykle bardzo wolne.
Serge Rogatch
4
W przypadku większości aplikacji sprawdzanie pliku nie ma decydującego wpływu na wydajność
anhoppe
29
Nie wszystkie aspekty aplikacji o wysokiej wydajności wymagają optymalizacji. Na przykład odczytanie wiersza polecenia lub pliku konfiguracyjnego może być skomplikowane i może nie wymagać szybkości, chociaż sama aplikacja może wymagać poprawy wydajności C ++. Unikanie wzmocnienia w takich przypadkach stanowi ponowne wynalezienie koła, wysoko na liście anty-wzorów.
evoskuil,
5
@SergeRogatch boost :: fileystem :: istnieje nie jest bardzo wolny. Zobacz szczegółowe wyniki mojego testu porównawczego.
hungptit
3
„Zwiększenie jest zwykle bardzo wolne” - to nieprawda, a nawet nie jest jasne, jaki jest zakres roszczenia… Zwiększenie zawiera wiele pakietów różnych autorów, ale jest sprawdzane pod kątem wysokiej jakości. „W przypadku większości aplikacji sprawdzanie plików nie ma decydującego wpływu na wydajność” - OP specjalnie poprosił o szybkość ze względu na sprawdzenie bardzo dużej liczby plików. „Jeśli wydajność nie jest krytyczna, nie ma również sensu używać C ++” - kolejny błędny komentarz (i nie na temat). Większość oprogramowania jest napisana w sklepach i jest częścią systemu, który wymaga wyboru języka.
Jim Balter
23

Bez używania innych bibliotek lubię używać następującego fragmentu kodu:

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

Działa to na wielu platformach dla systemów zgodnych z Windows i POSIX.

Viktor Liehr
źródło
Czy to działa na komputerze Mac? Nie mam Maca, ale spodziewałbym się, że Mac też będzie w stanie to uwzględnić unistd.h. Może pierwszy #ifdefpowinien być specyficzny dla systemu Windows?
mat
5
Mac OSX jest zgodny z POSIX.
schaiba
20

Taki sam jak sugerowany przez PherricOxide, ale w C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}
Ramon La Pietra
źródło
1
.c_str () jest funkcją C ++. Nie znam C ++, więc opublikowałem odpowiednik 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.
}
LOLOLOL
źródło
19
Jeśli naprawdę chcesz to zrobić, po prostu „zwróć plik (bool)” zamiast używać gałęzi if / else.
Nik Haldimann,
Nie zapomnij zamknąć pliku w przypadku prawdziwej sprawy. Jest to rodzaj wycieku pamięci, jeśli pozostawisz plik otwarty przez cały czas działania programu, nie wspominając o tym, że może on zablokować plik, uniemożliwiając jego odczytanie po dowiedzeniu się, że on istnieje. Add: file.close () na drugie miejsce.
Bill Moore
2
z drugiej strony może nie musisz go jawnie zamykać ... Zapomniałem, że ifstream to RAII (Resource Acquisition Is Initialization) ... i oczyści się, gdy wyjdzie poza zakres z destruktora ... co czy mogę powiedzieć ... W dzisiejszych czasach języki, które mi się zajmują, są prane mózgu ...
Bill Moore
@BillMoore Twój drugi komentarz jest poprawny; wiele innych komentarzy na tej stronie zauważyło, że close()nie jest konieczne.
Keith M,
To sprawdza dostępność, a nie istnienie. Na przykład, jeśli plik istnieje, ale nie można uzyskać do niego dostępu z powodu praw dostępu, zwróci false, błędnie twierdząc, że plik nie istnieje.
SasQ
7

Kolejne 3 opcje pod oknami:

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
źródło
OpenFile jest tylko ANSI i ograniczony do 128 znaków .
David Bremner,
5
GetFileAttributesWersja jest w zasadzie kanoniczny sposób to zrobić w systemie Windows.
Felix Dombek
Wiem, że to stare, ale co stanie się w trzecim przypadku, gdy użytkownik będzie mógł odczytać plik, ale nie będzie mógł odczytać atrybutów pliku?
Quest
6

Ty też możesz to zrobić bool b = std::ifstream('filename').good();. Bez instrukcji rozgałęzienia (np. Jeśli) musi działać szybciej, ponieważ trzeba go wywoływać tysiące razy.

parw
źródło
Jak pokazuje zaakceptowana odpowiedź, jest to nieprawda. Każdy poważny kompilator prawdopodobnie wyemituje ten sam kod, niezależnie od tego, czy wstawisz „ jeśli” czy „nie”. W porównaniu z wariantami zwykłego C, konstruowanie obiektu ifstream (nawet na stosie) powoduje dodatkowe obciążenie.
minexew
6

Jeśli potrzebujesz rozróżnić plik od katalogu, zastanów się, które z nich wykorzystują stat, które jest najszybszym standardowym narzędziem, jak wykazał 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;
}
użytkownik3902302
źródło
4

Potrzebuję szybkiej funkcji, która może sprawdzić, czy plik istnieje, a odpowiedź PherricOxide jest prawie tym, czego potrzebuję, z wyjątkiem tego, że nie porównuje wydajności boost :: fileystem :: istnieje i funkcji otwartych. Z wyników testu porównawczego możemy łatwo zauważyć, że:

  • Korzystanie z funkcji stat jest najszybszym sposobem sprawdzenia, czy plik istnieje. Zauważ, że moje wyniki są zgodne z odpowiedzią PherricOxide.

  • Wydajność funkcji boost :: fileystem :: istnieje jest bardzo zbliżona do funkcji stat i jest przenośna. Poleciłbym to rozwiązanie, jeśli biblioteki rozszerzeń są dostępne z twojego kodu.

Wyniki testu porównawczego uzyskane z jądrem Linux 4.17.0 i 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

Poniżej znajduje się mój kod testu porównawczego:

#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
źródło
4

Możesz użyć std::ifstream, podobnie jak is_open, failnp. Jak poniżej kodu („otwarte” oznacza, że ​​plik istnieje lub nie istnieje):

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

cytowany z tej odpowiedzi

Jayhello
źródło
3
all_of (begin(R), end(R), [](auto&p){ exists(p); })

gdzie Rjest twoja sekwencja rzeczy podobnych do ścieżki i exists()pochodzi z przyszłego standardowego lub obecnego wzmocnienia. Jeśli wyrzucisz własny, zachowaj prostotę,

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

Rozgałęzione rozwiązanie nie jest absolutnie straszne i nie pożera deskryptorów plików,

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
}
Jan
źródło
PathFileExistsjest ograniczony do MAX_PATH(260) znaków; GetFileAttributesnie ma tego ograniczenia.
Felix Dombek
GetFileAttributesjest również ograniczony do MAX_PATH. Dokumenty opisują obejście, jeśli używasz ścieżek bezwzględnych, kodu Unicode i dołączasz specjalny ciąg prefiksu do nazwy ścieżki. Wydaje mi się, że i tak mamy styczność z odpowiedziami specyficznymi dla systemu Windows.
Jan
1
GetFileAttributesWnie ma ograniczeń.
Laurie Stearn
1

W 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);
}
Abhijeet Kandalkar
źródło
5
Jest to mniej pouczające niż odpowiedź udzielona przez Vincenta 4 lata wcześniej.
Jim Balter
2
W C ++ 17 system plików nie jest już eksperymentalny
Quest
0

Korzystanie z MFC jest możliwe w następujący sposób

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

Gdzie FileNamejest ciąg reprezentujący plik, który sprawdzasz pod kątem istnienia

Andy Bantly
źródło
0

istnieje tylko jeden szybszy sposób, aby sprawdzić, czy plik istnieje, a jeśli masz uprawnienia do jego odczytu, sposób korzystania z języka C jest szybszy i można go również używać w dowolnej wersji w C ++

rozwiązanie : w C znajduje się biblioteka errno.h, która ma zewnętrzną (globalną) zmienną całkowitą o nazwie errno, która zawiera liczbę, której można użyć do rozpoznania rodzaju błędu

    #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;
    }
Imad Diraa
źródło
-4

Chociaż istnieje kilka sposobów, aby to zrobić, najbardziej wydajnym rozwiązaniem Twojego problemu byłoby prawdopodobnie użycie jednej ze wstępnie zdefiniowanych metod fstream, takich jak good () . Za pomocą tej metody możesz sprawdzić, czy określony plik istnieje, czy nie.

fstream file("file_name.txt");

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

Mam nadzieję, że uznasz to za przydatne.

miksiii
źródło
4
Ten kod utworzy plik, jeśli nie istnieje, więc wynik zawsze będzie prawdziwy. Musisz użyć ifstream lub poprawnie ustawić parametr openmode.
Lubo Antonov