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 */
}
Vincent
sumber
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.

PherricOxide
sumber
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");
Vincent
sumber
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();
}
Harryngh
sumber
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 ++.

Jim Balter
sumber
25

Bagi mereka yang suka meningkatkan:

 boost::filesystem::exists(fileName)
anhoppe
sumber
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.

Viktor Liehr
sumber
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);
}
Ramon La Pietra
sumber
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.
}
LOLOLOL
sumber
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;
}
ravin.wang
sumber
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.

parv
sumber
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;
}
pengguna3902302
sumber
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();   
hungptit
sumber
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

Jayhello
sumber
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
}
John
sumber
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);
}
Abhijeet Kandalkar
sumber
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

Andy Bantly
sumber
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;
    }
Imad Diraa
sumber
-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.

miksiii
sumber
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