使用标准C ++ / C ++ 11 / C检查文件是否存在的最快方法?

Fastest way to check if a file exist using standard C++/C++11/C?

我想找到最快的方法来检查一个文件是否存在于标准C++ 11、C++或C中。我有数千个文件,然后在对它们做一些事情之前,我需要检查它们是否存在。在下面的函数中,我可以写什么来代替/* SOMETHING */

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

stat()函数在我的系统(Linux,用g++编译)上提供了最好的性能,如果出于某种原因拒绝使用posix函数,那么标准的fopen调用是最好的选择。


我用这段代码,到目前为止它还可以用。这不使用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,最快的方法是fopen,因为这是唯一的方法(文件可能存在,但不可打开,但如果您需要"做一些事情",您可能真的希望打开它)。C++、POSIX、Windows都提供额外的选项。

当我在这的时候,让我指出一些你的问题。你说你想要最快的方法,你有数千个文件,但是你要求一个函数的代码来测试一个文件(这个函数只在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;
}

你也可以做bool b = std::ifstream('filename').good();。如果没有分支指令(比如if),它必须执行得更快,因为它需要被调用数千次。


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

其中,R是你的路径序列,类似于事物,而exists()是来自未来的std或当前的提升。如果你自己滚,保持简单,

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();

您可以使用std::ifstreamis_openfail等函数,例如下面的代码(cout"open"表示文件是否存在):

enter image description here

enter image description here

引用自此答案


使用MFC可以使用以下功能

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

其中,FileName是表示您正在检查的文件的字符串。


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

希望你觉得这个有用。