Standart C ++ / C ++ 11 / C kullanarak bir dosyanın var olup olmadığını kontrol etmenin en hızlı yolu?


453

Bir dosyanın standart C ++ 11, C ++ veya C'de olup olmadığını kontrol etmenin en hızlı yolunu bulmak istiyorum. Binlerce dosyam var ve üzerlerinde bir şey yapmadan önce hepsinin var olup olmadığını kontrol etmeliyim. /* SOMETHING */Aşağıdaki işlev yerine ne yazabilirim ?

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

2
boost::filesystemgibi görünüyor stat(). (Belgelerden yola çıkarak.) FS çağrıları için çok daha hızlı yapabileceğinizi sanmıyorum. Hızlı yaptığınız şeyi yapmanın yolu "binlerce dosyaya bakmaktan kaçınmaktır".
01'de millimoose

16
TOCTOU sorusu: Varolan () çek ile "üzerinde bir şey yapıyorsunuz" arasında dosyanın bağlantısı kaldırılmadığını nasıl anlarsınız ?
pilcrow

7
@pilcrow İyi bir nokta, ancak bu kadar fazla doğruluğa ihtiyaç duymayan oldukça geniş bir uygulama yelpazesi var. Örneğin git pushmuhtemelen ilk kirli kontrolden sonra çalışma ağacına dokunmadığınızdan emin olmayın.
01'de millimoose

9
'Ben olmaz bir C / C ++ uygulaması düşünemiyorum' - Windows bir POSIX ortamı sağlamaz.
Jim Balter

Yanıtlar:


778

Bu yöntemlerin her birini 100.000 kez, yarısı var olan dosyalar ve yarısı olmayan dosyalar üzerinde çalıştıran bir test programı bir araya getirdim.

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

5 üzerinden ortalama 100.000 çağrıyı yürütmek için gereken toplam sürenin sonuçları,

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()Fonksiyon sistemimde (ile derlenmiş Linux, en iyi performansı sağladı g++standart ile) fopennedense çöpler için POSIX fonksiyonları kullanmak durumunda çağrı en iyi bahis olmak.


31
Yukarıdaki yöntemlerin hiçbiri varlığını kontrol etmez, aksine erişilebilirliği kontrol etmez. Varlığını kontrol etmek için tek bir standart C veya C ++ yolu bilmiyorum.
2013

10
stat()varlığını kontrol ediyor gibi görünüyor.
el.pescado

105
Bunu kullanan herkesin #incis <stat / stat.h> ifadesini hatırlaması gerekir, aksi takdirde yanlış statüyü kullanmaya çalışır.
Katianie

23
Ben ifstream yöntemi için, f.close()f fonksiyonun sonunda kapsam dışına çıkar gibi gerekmez . Öyleyse bloğu return f.good()değiştirebilir ifmisiniz?
ilent2

11
Ayrıca yaklaşan standarttan en.cppreference.com/w/cpp/experimental/fs/exists adresini de kullanabilirsiniz / test edebilirsiniz
zahir

153

Not: C ++ 14'te ve TS dosya sistemi bitirilir ve kabul edilir edilmez, çözüm şu şekilde olacaktır:

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

ve sadece C ++ 17'den beri:

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


1
MS Visual Studio 2013'te bu işlev şu adresten edinilebilirstd::tr2::sys::exists("helloworld.txt");
Constantin

3
Umarım olmayacak std::exists, bu oldukça kafa karıştırıcı olurdu (düşünün: bir set gibi bir STL kapsayıcısında var).
einpoklum

3
Visual Studio 2015'te ayrıca:#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
Orwellophile

1
Unutmayın#include <experimental/filesystem>
Mohammed Noureldin

112

Bu kod parçasını kullanıyorum, şu ana kadar benimle çalışıyor. Bu, C ++ 'nın birçok süslü özelliğini kullanmaz:

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

8
Ancak, dosya başka bir program tarafından kilitlenmişse veya dosyaya erişim yoksa başarısız olabilir.
Jet

2
akışı kapatmanız gerekiyor mu?
Mo0gles

29
@ Mo0gles: ifstreamYıkıcı çıktıktan sonra çağrılır is_file_existve akışı kapatır.
Isaac

2
C ++ 11'den itibaren bool operatörünü kullanarak tek bir satırda yapabilirsiniz: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen

6
@Orwellophilereturn std::ifstream(fileName);
emlai

27

Dosyaların bulunduğu yere bağlıdır. Örneğin, hepsinin aynı dizinde olması gerekiyorsa, tüm dizin girdilerini bir karma tablosuna okuyabilir ve sonra tüm adları karma tablosuna göre kontrol edebilirsiniz. Bu , bazı sistemlerde her dosyayı ayrı ayrı kontrol etmekten daha hızlı olabilir . Her dosyayı ayrı ayrı kontrol etmenin en hızlı yolu sisteminize bağlıdır ... ANSI C yazıyorsanız, en hızlı yol fopentek yol olmasıdır (bir dosya var olabilir, ancak açılabilir olmayabilir, ancak muhtemelen "üzerinde bir şey yapmalısın"). C ++, POSIX, Windows ek seçenekler sunar.

Ben oyundayken, sorunuzla ilgili bazı problemlere dikkat çekeyim. En hızlı yolu istediğinizi ve binlerce dosyanız olduğunu söylüyorsunuz, ancak daha sonra tek bir dosyayı test etmek için bir işlev kodu istersiniz (ve bu işlev C değil, yalnızca C ++ için geçerlidir). Bu, çözüm hakkında bir varsayım yaparak gereksinimlerinizle çelişir ... bir XY sorunu vakası . Ayrıca "standart c ++ 11 (veya) c ++ (veya) c" ... de hepsi farklıdır ve bu da hız gereksiniminizle tutarsızdır ... en hızlı çözüm, kodu hedef sistemi. Sorudaki tutarsızlık, sisteme bağlı ve standart C veya C ++ olmayan çözümler veren bir yanıtı kabul ettiğiniz gerçeğiyle vurgulanmaktadır.


25

Güçlendirmeyi sevenler için:

 boost::filesystem::exists(fileName)

5
Boost genellikle son derece yavaştır.
Serge Rogatch

4
Çoğu uygulama için bir dosya var, performans açısından kritik değildir
anhoppe

29
Yüksek performanslı bir uygulamanın tüm yönleri optimizasyon gerektirmez. Örneğin, komut satırını veya bir yapılandırma dosyasını okumak karmaşık olabilir ve hız gerektirmeyebilir, ancak uygulamanın kendisi C ++ 'ın performans avantajlarını gerektirebilir. Bu gibi durumlarda Takviyeden kaçınmak, patinaj önleme listesinde yüksek tekerlek yeniden icatını oluşturur.
evoskuil

5
@SergeRogatch boost :: dosya sistemi :: mevcut çok yavaş değil. Ayrıntılı bilgi için karşılaştırma sonuçlarıma bakın.
hungptit

3
"Boost genellikle son derece yavaştır" - bu yanlıştır ve iddianın kapsamının ne olduğu bile net değildir ... Boost, farklı yazarlar tarafından birçok paket içerir, ancak yüksek kalite için denetlenir. "Çoğu uygulama için bir dosya vardır, performans açısından kritik değildir" - OP çok fazla sayıda dosyayı kontrol etmesi nedeniyle özellikle hız istendi. "Performans kritik değilse, o zaman C ++ kullanmanın bir anlamı yoktur" - başka bir hatalı yorum (ve konu dışı). Çoğu yazılım mağazalarda yazılmıştır ve dil seçimini zorunlu kılan bir sistemin parçasıdır .
Jim Balter

23

Diğer kütüphaneleri kullanmadan, aşağıdaki kod snippet'ini kullanmayı seviyorum:

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

Bu, Windows ve POSIX uyumlu sistemler için çapraz platformda çalışır.


Bu Mac'te çalışıyor mu? Bir mac'um yok, ama bir macun unistd.hda dahil edebilmesini beklerdim . Belki birincisi #ifdefpencerelere özel olmalı?
mat

5
Mac OSX POSIX uyumludur.
schaiba

20

PherricOxide tarafından önerilenle aynı, ancak C

#include <sys/stat.h>
int exist(const char *name)
{
  struct stat   buffer;
  return (stat (name, &buffer) == 0);
}

1
.c_str () bir C ++ işlevidir. C ++ bilmiyorum, bu yüzden C eşdeğeri yayınladım.
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
Bunu gerçekten yapacaksanız, bir if / else dalı kullanmak yerine "return (bool) dosyası" nı kullanmanız yeterlidir.
Nik Haldimann

Gerçek vaka durumunda dosyayı kapatmayı unutmayın. Bu, programın tüm çalışma süresi boyunca dosyayı açık bırakırsanız, dosyanın var olduğunu bildikten sonra okuyamayacağınız için dosyanızı kilitleyebileceğini belirtmemek için bir bellek sızıntısı türüdür .. add: file.close () ikinci olarak.
Bill Moore

2
ikinci düşüncede belki de açıkça kapatmanıza gerek yoktur ... Eğer bir akışın bir RAII (Kaynak Edinimi Başlatmadır) ... ve yıkıcıdan kapsam dışına çıktıkça kendini temizleyeceğini unuttum ... ne diyebilir miyim ... Bugünlerde çöp toplayıcı dilleri tarafından beyin yıkama yapıyorum ...
Bill Moore

@BillMoore İkinci yorumunuz doğru; Bu sayfadaki diğer birçok yorumun close()gerekli olmadığını belirtti .
Keith M

Bu, varlığı değil erişilebilirliği kontrol eder. Örneğin, dosya varsa, ancak erişim hakları nedeniyle erişilemiyorsa, yanlışlıkla dosyanın olmadığını iddia ederek yanlış döndürür.
SasQ

7

Pencerelerin altındaki diğer 3 seçenek:

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

OpenFile yalnızca ANSI'dir ve 128 karakterle sınırlıdır .
David Bremner

5
GetFileAttributesVersiyon temelde Windows bunu yapmak için kurallı yoldur.
Felix Dombek

Bu eski olduğunu biliyorum ama kullanıcı dosyayı okuma yeteneğine sahip ancak dosya özniteliklerini okumak için izin verilmez 3. durumda ne olacak?
Görev

6

Siz de yapabilirsiniz bool b = std::ifstream('filename').good();. Şube talimatları olmadan (sanki) binlerce kez çağrılması gerektiğinden daha hızlı performans göstermelidir.


Kabul edilen cevabın gösterdiği gibi, bu doğru değildir. Herhangi bir ciddi derleyici, if olsun ya da olmasın , muhtemelen aynı kodu yayar . Plain-C varyantlarıyla karşılaştırıldığında, ifstream nesnesinin (yığınta bile olsa) oluşturulması ek yüke neden olur.
minexew

6

Bir dosya ve dizin arasında ayrım yapmanız gerekirse, her ikisinin de PherricOxide tarafından gösterildiği gibi en hızlı standart aracın stat'ını kullandığı aşağıdakileri göz önünde bulundurun:

#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

Ben bir dosya var olup olmadığını kontrol edebilirsiniz hızlı bir fonksiyona ihtiyacım var ve PherricOxide'in yanıtı hemen hemen neye ihtiyacım olduğunu artırmak dışında boost :: filesystem :: ve açık fonksiyonların performansını karşılaştırmak. Kıyaslama sonuçlarından şunu kolayca görebiliriz:

  • Stat fonksiyonunu kullanmak, bir dosyanın mevcut olup olmadığını kontrol etmenin en hızlı yoludur. Sonuçlarımın PherricOxide'in cevabı ile tutarlı olduğunu unutmayın.

  • Boost :: filesystem :: exist işlevinin performansı stat işlevine çok yakındır ve taşınabilirdir. Kodunuzdan yükseltme kitaplıklarına erişilebiliyorsa bu çözümü tavsiye ederim.

Linux çekirdeği 4.17.0 ve gcc-7.3 ile elde edilen karşılaştırma sonuçları:

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

Karşılaştırma kodum aşağıdadır:

#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

Sen kullanabilirsiniz std::ifstream, gibi görevi görüyordu is_open, failkod (cout "açık" anlamına gelir Dosya mevcut veya değil) aşağıdaki gibi örneğin:

resim açıklamasını buraya girin

resim açıklamasını buraya girin

bu cevaptan alıntı yapıldı


3
all_of (begin(R), end(R), [](auto&p){ exists(p); })

Ryol benzeri şeylerden oluşan diziniz nerede exists()ve gelecekteki standart veya mevcut destekten. Kendinizi yuvarlarsanız, basit tutun,

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

Dallı çözüm kesinlikle korkunç değil ve dosya tanımlayıcılarını silip süpürmez,

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
}

PathFileExistsMAX_PATH(260) karakter ile sınırlıdır ; GetFileAttributesbu sınırlamaya sahip değildir.
Felix Dombek

GetFileAttributesde MAX_PATH ile sınırlıdır. Belgeler, mutlak yollar, unicode kullanır ve yol adına özel bir önek dizesi eklerseniz geçici bir çözümü açıklar . Bence zaten Windows'a özgü yanıtlarla teğet geçiyoruz.
John

1
GetFileAttributesWsınırlaması yoktur.
Laurie Stearn

1

C ++ 17'de:

#include <experimental/filesystem>

bool is_file_exist(std::string& str) {   
    namespace fs = std::experimental::filesystem;
    fs::path p(str);
    return fs::exists(p);
}

5
Bu, Vincent'ın 4 yıl önce verdiği cevaptan daha az bilgilendiricidir.
Jim Balter

2
C ++ 17 dosya sisteminde artık deneysel değil
Quest

0

MFC kullanarak aşağıdakiler mümkündür

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

FileNameVarlık için denetlediğiniz dosyayı temsil eden bir dize nerede


0

Dosyanın var olup olmadığını kontrol etmenin sadece bir hızlı yolu vardır ve okuma izniniz varsa C dilinin kullanılması daha hızlıdır ve C ++ 'da herhangi bir sürümde de kullanılabilir

çözüm : C'de , hata türünü tanımak için kullanılabilecek bir sayı içeren errno adında harici (global) bir tamsayı değişkenine sahip bir errno.h kütüphanesi vardır.

    #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

Bunu yapmanın birkaç yolu olmasına rağmen, probleminize en etkili çözüm muhtemelen fstream öğesinin good () gibi önceden tanımlanmış yöntemlerinden birini kullanmak olacaktır . Bu yöntemle, belirttiğiniz dosyanın mevcut olup olmadığını kontrol edebilirsiniz.

fstream file("file_name.txt");

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

Umarım bunu faydalı bulursun.


4
Bu kod, yoksa dosyayı oluşturur, bu nedenle sonuç her zaman doğru olur. İfstream öğesini kullanmanız veya openmode parametresini doğru ayarlamanız gerekir.
Lubo Antonov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.