Fastest way to check if a file exist using standard C++/C++11/C?
我想找到最快的方法来检查一个文件是否存在于标准C++ 11、C++或C中。我有数千个文件,然后在对它们做一些事情之前,我需要检查它们是否存在。在下面的函数中,我可以写什么来代替
1 2 3 4 | inline bool exist(const std::string& name) { /* SOMETHING */ } |
我把这些方法中的每一个都运行了100000次的测试程序放在一起,一半运行在已有的文件上,另一半运行在没有的文件上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #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); } |
运行100000个呼叫的总时间的结果平均超过5次运行,
1 2 3 4 | 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** |
我用这段代码,到目前为止它还可以用。这不使用C++的许多花哨特征:
1 2 3 4 5 | bool is_file_exist(const char *fileName) { std::ifstream infile(fileName); return infile.good(); } |
备注:在C++ 14中,一旦文件系统TS将被完成和采用,解决方案将使用:
1 | std::experimental::filesystem::exists("helloworld.txt"); |
而且因为C++ 17,只有:
1 | std::filesystem::exists("helloworld.txt"); |
这取决于文件所在的位置。例如,如果它们都应该在同一个目录中,则可以将所有目录条目读取到哈希表中,然后根据哈希表检查所有名称。在某些系统上,这可能比单独检查每个文件更快。检查每个文件的最快方法取决于您的系统…如果您正在编写ANSIC,最快的方法是
当我在这的时候,让我指出一些你的问题。你说你想要最快的方法,你有数千个文件,但是你要求一个函数的代码来测试一个文件(这个函数只在C++中有效,而不是C)。这与您的要求相矛盾,因为您对解决方案做出了假设…xy问题的一个例子。你也会说:"在标准C++ 11(或)C++(或)C"中…这都是不同的,这也不符合你对速度的要求…最快的解决方案是将代码裁剪到目标系统。这个问题的不一致性突出的事实是,你接受了一个答案,它给出了依赖于系统的解决方案,而不是标准的C或C++。
对于喜欢Boost的人:
1 | boost::filesystem::exists(fileName) |
不使用其他库,我喜欢使用以下代码段:
1 2 3 4 5 6 7 8 9 10 11 | #ifdef _WIN32 #include <io.h> #define access _access_s #else #include <unistd.h> #endif bool FileExists( const std::string &Filename ) { return access( Filename.c_str(), 0 ) == 0; } |
这适用于Windows和POSIX兼容系统的跨平台。
与信息素建议的相同,但在C中
1 2 3 4 5 6 | #include <sys/stat.h> int exist(const char *name) { struct stat buffer; return (stat (name, &buffer) == 0); } |
1 2 3 4 5 6 7 8 | 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. } |
Windows下的其他3个选项:
一1 2 3 4 5 | 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; } |
二
1 2 3 4 5 6 7 8 9 10 | 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; } |
三
1 2 3 4 | inline bool exist(const std::string& name) { return GetFileAttributes(name.c_str()) != INVALID_FILE_ATTRIBUTES; } |
如果您需要区分一个文件和一个目录,请考虑以下两种情况,它们都使用stat,这是费里考氧化物所演示的最快的标准工具:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #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; } |
你也可以做
1 | all_of (begin(R), end(R), [](auto&p){ exists(p); }) |
其中,
1 | bool exists (string const& p) { return ifstream{p}; } |
分支解决方案并不是非常糟糕,它不会吞食文件描述符,
1 2 3 4 5 6 7 8 | 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 } |
我需要一个快速的函数,它可以检查文件是否存在,而费里考的答案几乎就是我需要的,除非它不比较boost::filesystem::exist s和open函数的性能。从基准结果中我们可以很容易地看到:
使用stat函数是检查文件是否存在的最快方法。请注意,我的结果与费里考的答案是一致的。
boost::filesystem::exists函数的性能与stat函数的性能非常接近,并且是可移植的。如果可以从代码中访问Boost库,我建议使用此解决方案。
Linux内核4.17.0和GCC-7.3的基准测试结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
以下是我的基准代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #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(); |
您可以使用
引用自此答案
使用MFC可以使用以下功能
1 2 | CFileStatus FileStatus; BOOL bFileExists = CFile::GetStatus(FileName,FileStatus); |
其中,
在C++ 17中:
1 2 3 4 5 6 7 | #include <experimental/filesystem> bool is_file_exist(std::string& str) { namespace fs = std::experimental::filesystem; fs::path p(str); return fs::exists(p); } |
尽管有几种方法可以做到这一点,但解决问题的最有效方法可能是使用fstream的预定义方法之一,如good()。使用此方法,可以检查指定的文件是否存在。
1 2 3 4 5 6 7 8 9 10 | fstream file("file_name.txt"); if (file.good()) { std::cout <<"file is good." << endl; } else { std::cout <<"file isnt good" << endl; } |
希望你觉得这个有用。