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


453

我想找到一种检查标准C ++ 11,C ++或C中是否存在文件的最快方法。我有成千上万个文件,在对它们进行处理之前,我需要检查所有文件是否都存在。我可以写些什么而不是/* SOMETHING */下面的函数?

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

2
boost::filesystem似乎用stat()。(根据文档。)我认为您无法更快地完成FS调用。快速执行操作的方法是“避免查看数千个文件”。
Millimoose 2012年

16
TOCTOU问题:您如何知道在exist()检查和“在其上做某事”之间未解除链接?
pilcrow,2012年

7
@pilcrow很好,但是有相当多的应用程序不需要那么多的正确性。例如,git push在进行最初的脏检查后,可能不会费心确保您没有触摸工作树。
Millimoose 2012年

9
“我想不出没有它的C / C ++实现”-Windows不提供POSIX环境。
吉姆·巴尔特

Answers:


777

好吧,我整理了一个测试程序,每种方法都运行100,000次,一半在存在的文件上,一半在不存在的文件上。

#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次运行中平均运行100,000次呼叫的总时间结果,

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++)上提供了最佳性能,fopen如果由于某种原因而拒绝使用POSIX函数,则最好的选择是标准调用。


31
上面的方法都不检查是否存在,而是检查可访问性。不过,我不知道一种用于检查是否存在的单一标准C或C ++方法。
IInspectable 2013年

10
stat()似乎要检查是否存在。
el.pescado

105
使用此方法的任何人都需要记住#include <sys / stat.h>,否则它将尝试使用错误的统计信息。
Katianie 2014年

23
我想对于ifstream方法,f.close()由于f在函数末尾超出范围,您不需要。那么return f.good()可以更换if块吗?
ilent2 2014年

11
您还可以使用/测试en.cppreference.com/w/cpp/experimental/fs/exists从即将到来的标准
查希尔·

153

备注:在C ++ 14中,一旦文件系统TS完成并被采用,解决方案将使用:

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

从C ++ 17开始,仅:

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


1
在MS Visual Studio 2013中,此功能在std::tr2::sys::exists("helloworld.txt");
Constantin

3
我实际上希望不会那样std::exists,那会很令人困惑(想想:像集合一样存在于STL容器中)。
einpoklum 2016年

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

1
不要忘了#include <experimental/filesystem>
穆罕默德Noureldin

112

我使用这段代码,到目前为止对我来说还可以。这没有使用C ++的许多高级功能:

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

8
但是,如果文件被另一个程序锁定或无法访问该文件,则可能会失败。
2015年

2
您需要关闭信息流吗?
Mo0gles 2015年

29
@ Mo0gles:ifstream析构函数将在退出时被调用,is_file_exist并将关闭流。
以撒

2
从C ++ 11开始,您可以使用bool运算符在一行中完成此操作:en.cppreference.com/w/cpp/io/basic_ios/operator_bool
Mugen

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

27

这取决于文件所在的位置。例如,如果假定它们都在同一个目录中,则可以将所有目录条目读入哈希表,然后对照哈希表检查所有名称。在某些系统上,这可能比分别检查每个文件更快。单独检查每个文件的最快方法取决于您的系统...如果要编写ANSI C,最快的方法是fopen因为这是唯一的方法(文件可能存在但不可打开,但是如果您确实希望打开文件,需要“做一些事情”)。C ++,POSIX,Windows均提供其他选项。

在讨论过程中,请允许我指出您的问题。您说您想要最快的方法,并且您有成千上万个文件,但是随后您要求提供用于测试单个文件的函数的代码(该函数仅在C ++中有效,而在C中无效)。通过对解决方案进行假设(这是XY问题的情况)来与您的要求相矛盾。您还说了“在标准c ++ 11(or)c ++(or)c中” ...两者都是不同的,这也与您对速度的要求不一致...最快的解决方案将包括针对目标系统。您接受了一个答案,该答案给出的解决方案依赖于系统并且不是标准的C或C ++,这一事实突出表明了问题中的不一致。


25

对于那些喜欢助推器的人:

 boost::filesystem::exists(fileName)

5
加速通常非常慢。
Serge Rogatch

4
对于大多数应用程序来说,文件是否存在检查对性能并不重要
anhoppe

29
并非高性能应用程序的所有方面都需要优化。例如,尽管应用程序本身可能需要C ++的性能优势,但读取命令行或配置文件可能很复杂并且可能不需要速度。在这种情况下,避免使用Boost会重塑轮子,在反模式列表中占上风。
evoskuil

5
@SergeRogatch boost :: filesystem :: exists并不是很慢。有关详细信息,请参见我的基准测试结果。
Hangptit

3
“升压通常非常慢”-这是错误的,甚至不清楚索赔的范围是什么。Boost包含许多不同作者的软件包,但经过了高质量的审查。“对于大多数应用程序来说,检查文件是否存在性能并不是至关重要的” –由于检查了大量文件,OP特别要求速度。“如果性能不是很关键,那么使用C ++也是没有意义的” –另一个错误的注释(和主题无关)。大多数软件是在商店中编写的,并且是要求语言选择的系统的一部分。
吉姆·巴尔特

23

在不使用其他库的情况下,我喜欢使用以下代码片段:

#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兼容系统的跨平台。


在Mac上可以使用吗?我没有Mac,但我希望Mac unistd.h也能够包含Mac 。也许第一个#ifdef应该是特定于Windows的?
2016年

5
Mac OSX兼容POSIX。
schaiba


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
如果您真的要这样做,只需“返回(布尔)文件”,而不要使用if / else分支。
Nik Haldimann 2014年

如果情况确实如此,请不要忘记关闭文件。如果您在程序的整个运行期间将文件保持打开状态,那是一种内存泄漏,更不用说它可能会锁定您的文件,以便您在知道文件存在之后就无法读取它。.add:file.close()第二。
比尔·摩尔

2
再三考虑,也许您不需要显式关闭它...我忘了ifstream是RAII(资源获取是初始化)...并且会在析构函数超出范围时进行自我清理...我能说...这些天我被垃圾收集者的语言洗脑了……
比尔·摩尔

@BillMoore您的第二条评论是正确的;该页面上的许多其他注释都close()没有必要指出。
基思M

这将检查可访问性,而不是存在性。例如,如果文件存在,但是由于访问权限而无法访问,它将返回false,错误地声称该文件不存在。
SasQ

7

Windows下的另外3个选项:

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仅是ANSI,限制为128个字符
David Bremner

5
GetFileAttributes版本基本上是Windows中执行该操作的规范方法。
Felix Dombek

我知道这很旧,但是如果第3种情况下用户具有读取文件的能力但不被允许读取文件属性,将会发生什么?
探索

6

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


正如公认的答案所示,这是不正确的。无论您是否输入if,任何严肃的编译器都可能会发出相同的代码。与普通C变体相比,构造ifstream对象(即使在堆栈上)也会产生额外的开销。
minexew

5

如果需要区分文件和目录,请考虑以下两个都使用stat的方法,这是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

我需要一个可以检查文件是否存在的快速函数,而PherricOxide的答案几乎是我所需要的,只是它不比较boost :: filesystem :: exists和打开函数的性能。从基准测试结果中,我们可以轻松地看到:

  • 使用stat函数是检查文件是否存在的最快方法。请注意,我的结果与PherricOxide的答案一致。

  • boost :: filesystem :: exists函数的性能与stat函数非常接近,并且具有可移植性。如果可以从您的代码访问boost库,我将推荐此解决方案。

使用Linux内核4.17.0和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

下面是我的基准代码:

#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

您可以使用std::ifstream,功能is_openfail例如,例如以下代码(cout“ open”表示文件存在或不存在):

在此处输入图片说明

在此处输入图片说明

从这个答案中引用


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

R您的类似路径的序列在哪里,exists()来自未来的标准或当前的提升。如果自己动手,请保持简单,

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

分支解决方案并非绝对糟糕,也不会吞噬文件描述符,

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
}

PathFileExists限制为MAX_PATH(260)个字符;GetFileAttributes没有这个限制。
Felix Dombek

GetFileAttributes也限于MAX_PATH。如果您使用绝对路径,unicode并在路径名称前添加特殊的前缀字符串,则文档描述了一种解决方法。我认为我们还是要与Windows特定的响应切线。
John

1
GetFileAttributesW没有限制。
Laurie Stearn '18


0

使用MFC可以通过以下方式

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

FileName代表您要检查存在性的文件的字符串在哪里


0

只有一种更快的方法来检查文件是否存在以及您是否有权读取它,即使用C语言的方式希望更快,并且可以在C ++的任何版本中使用

解决方案:在C语言中,有一个库errno.h,它具有一个名为errno的外部(全局)整数变量,该变量包含一个可用于识别错误类型的数字。

    #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

尽管有多种方法可以执行此操作,但最有效的解决方案可能是使用fstream的预定义方法之一,例如good()。使用此方法,您可以检查指定的文件是否存在。

fstream file("file_name.txt");

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

希望这个对你有帮助。


4
如果该文件不存在,此代码将创建该文件,因此结果将始终为true。您需要使用ifstream或正确设置openmode参数。
卢博·安东诺夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.