Cara tercepat untuk memeriksa apakah ada file menggunakan standar C ++ / C ++ 11 / C?


453

Saya ingin menemukan cara tercepat untuk memeriksa apakah ada file dalam standar C ++ 11, C ++, atau C. Saya memiliki ribuan file dan sebelum melakukan sesuatu pada mereka, saya perlu memeriksa apakah semuanya ada. Apa yang bisa saya tulis alih-alih /* SOMETHING */dalam fungsi berikut?

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

2
boost::filesystemtampaknya digunakan stat(). (Dengan asumsi dari dokumentasi.) Saya tidak berpikir Anda bisa melakukan lebih cepat untuk panggilan FS. Cara untuk mempercepat apa yang Anda lakukan adalah "hindari melihat ribuan file."
milimoose

16
Pertanyaan TOCTOU : bagaimana Anda tahu file tersebut tidak terputus antara cek ada () Anda dan "melakukan sesuatu padanya" ?
pilcrow

7
@pilcrow Poin bagus, tetapi ada beragam aplikasi yang tidak membutuhkan banyak kebenaran. Misalnya git pushmungkin tidak repot-repot memastikan Anda tidak menyentuh pohon kerja setelah pemeriksaan kotor awal.
milimoose

9
'Saya tidak bisa memikirkan implementasi C / C ++ yang tidak akan memilikinya' - Windows tidak menyediakan lingkungan POSIX.
Jim Balter

Jawaban:


778

Yah saya mengumpulkan program pengujian yang menjalankan masing-masing metode ini 100.000 kali, setengah pada file yang ada dan setengah pada file yang tidak.

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

Hasil untuk total waktu untuk menjalankan 100.000 panggilan dengan rata-rata lebih dari 5 kali,

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

The stat()fungsi yang disediakan kinerja terbaik pada sistem saya (Linux, yang disusun dengan g++), dengan standar fopenpanggilan menjadi taruhan terbaik Anda jika Anda untuk beberapa alasan menolak untuk menggunakan fungsi POSIX.


31
Tak satu pun dari metode di atas memeriksa keberadaan, melainkan aksesibilitas. Saya tidak tahu cara standar C atau C ++ tunggal untuk memeriksa keberadaan.
IInspectable

10
stat()tampaknya memeriksa keberadaan.
el.pescado

105
Siapa pun yang menggunakan ini harus ingat untuk memasukkan # sys / stat.h> jika tidak ia mencoba menggunakan stat yang salah.
Katianie

23
Saya membayangkan untuk metode ifstream, Anda tidak perlu f.close()karena f keluar dari ruang lingkup di akhir fungsi. Jadi return f.good()bisakah ganti ifblok?
ilent2

11
Anda juga dapat menggunakan / test en.cppreference.com/w/cpp/experimental/fs/exists dari standar yang akan datang
zahir

153

Catatan: di C ++ 14 dan segera setelah filesystem TS selesai dan diadopsi, solusinya adalah menggunakan:

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

dan sejak C ++ 17, hanya:

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

5
sudah tersedia di Boost.Filesystem
TemplateRex

1
Dalam MS Visual Studio 2013 fungsi ini tersedia di bawahstd::tr2::sys::exists("helloworld.txt");
Constantin

3
Saya benar-benar berharap itu tidak akan std::exists, itu akan sangat membingungkan (pikirkan: ada dalam wadah STL seperti set).
einpoklum

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

1
Jangan lupa untuk#include <experimental/filesystem>
Mohammed Noureldin

112

Saya menggunakan kode ini, ia bekerja dengan baik sejauh ini. Ini tidak menggunakan banyak fitur mewah C ++:

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

8
Namun, mungkin gagal jika file dikunci oleh program lain atau jika tidak ada akses ke file tersebut.
Jet

2
apakah Anda perlu menutup aliran?
Mo0gles

29
@ Mo0gles: Destuktor ifstreamakan dipanggil saat keluar is_file_existdan akan menutup aliran.
Isaac

2
Pada C ++ 11 Anda dapat melakukannya dalam satu baris menggunakan operator bool: en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen

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

27

Itu tergantung di mana file berada. Misalnya, jika mereka semua seharusnya berada di direktori yang sama, Anda dapat membaca semua entri direktori ke dalam tabel hash dan kemudian memeriksa semua nama terhadap tabel hash. Ini mungkin lebih cepat pada beberapa sistem daripada memeriksa setiap file satu per satu. Cara tercepat untuk memeriksa setiap file secara terpisah tergantung pada sistem Anda ... jika Anda menulis ANSI C, cara tercepat adalah fopenkarena itu satu-satunya cara (file mungkin ada tetapi tidak dapat dibuka, tetapi Anda mungkin benar-benar ingin dapat dibuka jika Anda perlu "melakukan sesuatu di atasnya"). C ++, POSIX, Windows semua menawarkan opsi tambahan.

Sementara saya melakukannya, izinkan saya menunjukkan beberapa masalah dengan pertanyaan Anda. Anda mengatakan bahwa Anda menginginkan cara tercepat, dan Anda memiliki ribuan file, tetapi kemudian Anda meminta kode untuk fungsi untuk menguji satu file (dan fungsi itu hanya valid dalam C ++, bukan C). Ini bertentangan dengan kebutuhan Anda dengan membuat asumsi tentang solusi ... kasus masalah XY . Anda juga mengatakan "dalam standar c ++ 11 (atau) c ++ (atau) c" ... yang semuanya berbeda, dan ini juga tidak konsisten dengan kebutuhan Anda akan kecepatan ... solusi tercepat akan melibatkan menyesuaikan kode dengan sistem target. Ketidakkonsistenan dalam pertanyaan disorot oleh fakta bahwa Anda menerima jawaban yang memberikan solusi yang bergantung pada sistem dan bukan standar C atau C ++.


25

Bagi mereka yang suka meningkatkan:

 boost::filesystem::exists(fileName)

5
Peningkatan biasanya sangat lambat.
Serge Rogatch

4
Untuk sebagian besar aplikasi, ada file, periksa bukan kinerja kritis
anhoppe

29
Tidak semua aspek aplikasi berkinerja tinggi memerlukan pengoptimalan. Misalnya, membaca baris perintah atau file konfigurasi bisa rumit dan mungkin tidak memerlukan kecepatan, meskipun aplikasi itu sendiri mungkin memerlukan keunggulan kinerja C ++. Menghindari Peningkatan dalam kasus-kasus semacam itu merupakan penemuan kembali roda, yang paling tinggi dalam daftar anti-pola.
evoskuil

5
@SergeRogatch boost :: filesystem :: ada tidak terlalu lambat. Lihat hasil patokan saya untuk informasi detail.
hungptit

3
"Boost biasanya sangat lambat" - ini salah, dan bahkan tidak jelas apa ruang lingkup klaimnya ... Boost berisi banyak paket oleh penulis yang berbeda tetapi diperiksa untuk kualitas tinggi. "Untuk sebagian besar aplikasi, ada file, periksa bukan kinerja kritis" - OP secara khusus meminta kecepatan karena memeriksa sejumlah besar file. "Jika kinerja tidak kritis, maka tidak ada gunanya menggunakan C ++" - komentar keliru lainnya (dan di luar topik). Sebagian besar perangkat lunak ditulis di toko - toko dan merupakan bagian dari sistem yang mengamanatkan pilihan bahasa.
Jim Balter

23

Tanpa menggunakan pustaka lain, saya suka menggunakan potongan kode berikut:

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

Ini berfungsi lintas platform untuk sistem yang kompatibel dengan Windows dan POSIX.


Apakah ini berfungsi di Mac? Saya tidak punya mac, tapi saya berharap mac bisa memasukkan unistd.hjuga. Mungkin yang pertama #ifdefharus spesifik windows?
matth

5
Mac OSX kompatibel dengan POSIX.
schaiba

20

Sama seperti yang disarankan oleh PherricOxide tetapi dalam C

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

1
.c_str () adalah fungsi C ++. Saya tidak tahu C ++ jadi saya memposting setara 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
Jika Anda benar-benar akan melakukannya, cukup "kembalikan file (bool)" daripada menggunakan cabang if / else.
Nik Haldimann

Jangan lupa untuk menutup file jika terjadi kasus yang sebenarnya. Itu adalah jenis kebocoran memori jika Anda membiarkan file terbuka untuk seluruh runtime program, belum lagi itu dapat mengunci file Anda sehingga Anda tidak dapat membacanya setelah mengetahui bahwa itu ada .. tambahkan: file.close () untuk yang kedua.
Bill Moore

2
pada pemikiran kedua mungkin Anda tidak perlu menutupnya secara eksplisit ... Saya lupa bahwa ifstream adalah RAII (Resource Acquisition Is Inisialisasi) ... dan akan membersihkan diri saat keluar dari ruang lingkup dari destructor ... apa dapatkah saya mengatakan ... saya dicuci otak oleh bahasa pengumpul sampah akhir-akhir ini ...
Bill Moore

@BillMoore Komentar kedua Anda benar; banyak komentar lain di halaman ini telah mencatat close()tidak perlu.
Keith M

Ini memeriksa aksesibilitas, bukan keberadaan. Misalnya, jika file itu ada, tetapi tidak dapat diakses karena hak akses, itu akan kembali palsu, keliru mengklaim bahwa file itu tidak ada.
SasQ

7

3 pilihan lain di bawah 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;
}

OpenFile hanya ANSI dan terbatas hingga 128 karakter .
David Bremner

5
The GetFileAttributesVersi pada dasarnya adalah cara kanonik untuk melakukannya pada Windows.
Felix Dombek

Saya tahu ini sudah tua tetapi apa yang akan terjadi pada kasus ke-3 ketika pengguna memiliki kemampuan untuk membaca file tetapi tidak diizinkan untuk membaca atribut file?
Quest

6

Anda mungkin juga melakukannya bool b = std::ifstream('filename').good();. Tanpa instruksi cabang (seperti jika) itu harus bekerja lebih cepat karena perlu dipanggil ribuan kali.


Seperti yang ditunjukkan oleh jawaban yang diterima, ini tidak benar. Kompilator serius apa pun mungkin akan memancarkan kode yang sama apakah Anda memasukkan if atau tidak. Dibandingkan dengan varian plain-C, membangun objek ifstream (bahkan jika di stack) menimbulkan overhead tambahan.
minexew

5

Jika Anda perlu membedakan antara file dan direktori, pertimbangkan hal berikut yang keduanya menggunakan stat yang merupakan alat standar tercepat seperti yang ditunjukkan oleh 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

Saya memerlukan fungsi cepat yang dapat memeriksa apakah ada file atau tidak dan jawaban PherricOxide hampir apa yang saya butuhkan kecuali tidak membandingkan kinerja boost :: filesystem :: ada dan fungsi terbuka. Dari hasil patokan kita dapat dengan mudah melihat bahwa:

  • Menggunakan fungsi stat adalah cara tercepat untuk memeriksa apakah ada file. Perhatikan bahwa hasil saya konsisten dengan jawaban PherricOxide.

  • Kinerja fungsi boost :: filesystem :: ada sangat dekat dengan fungsi stat dan juga portabel. Saya akan merekomendasikan solusi ini jika meningkatkan perpustakaan dapat diakses dari kode Anda.

Hasil benchmark diperoleh dengan kernel Linux 4.17.0 dan 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

Di bawah ini adalah kode benchmark saya:

#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

Anda dapat menggunakan std::ifstream, seperti fungsi is_open,, failmisalnya seperti kode di bawah ini ("terbuka" berarti file ada atau tidak):

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

dikutip dari jawaban ini


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

di mana Rurutan hal-hal seperti jalur Anda, dan exists()dari masa depan std atau dorongan saat ini. Jika Anda menggulung sendiri, sederhanakan,

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

Solusi bercabang tidak benar-benar mengerikan dan tidak akan melahap deskriptor file,

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
}

PathFileExiststerbatas pada MAX_PATH(260) karakter; GetFileAttributestidak memiliki batasan ini.
Felix Dombek

GetFileAttributesterbatas pada MAX_PATH juga. Dokumen menjelaskan solusi jika Anda menggunakan jalur absolut, unicode, dan menambahkan string awalan khusus ke nama jalur. Saya pikir kita tidak setuju dengan respons khusus Windows.
John

1
GetFileAttributesWtidak memiliki batasan.
Laurie Stearn

1

Di 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
Ini kurang informatif daripada jawaban yang diberikan oleh Vincent 4 tahun sebelumnya.
Jim Balter

2
Dalam C ++ 17 filesystem tidak lagi eksperimental
Quest

0

Menggunakan MFC dimungkinkan dengan yang berikut ini

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

Di mana FileNamestring mewakili file yang Anda periksa keberadaannya


0

hanya ada satu cara yang lebih cepat untuk memeriksa apakah file itu ada dan jika Anda memiliki izin untuk membacanya caranya menggunakan bahasa C yang ingin lebih cepat dan dapat digunakan juga dalam versi apa pun di C ++

solusi : di C ada pustaka errno.h yang memiliki variabel integer eksternal (global) yang disebut errno yang berisi angka yang dapat digunakan untuk mengenali jenis kesalahan

    #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

Meskipun ada beberapa cara untuk melakukan ini, solusi paling efisien untuk masalah Anda mungkin akan menggunakan salah satu metode yang telah ditentukan fstream seperti good () . Dengan metode ini Anda dapat memeriksa apakah file yang Anda tentukan ada atau tidak.

fstream file("file_name.txt");

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

Saya harap Anda menemukan ini berguna.


4
Kode ini akan membuat file jika tidak ada, sehingga hasilnya akan selalu benar. Anda perlu menggunakan ifstream, atau mengatur parameter openmode dengan benar.
Lubo Antonov
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.