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

Vin*_*ent 407 c c++ file stream

我想找到检查文件是否存在于标准C++ 11,C++或C中的最快方法.我有数千个文件,在对它们做一些事情之前我需要检查它们是否全部存在.我可以写什么而不是/* SOMETHING */以下功能?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}
Run Code Online (Sandbox Code Playgroud)

Phe*_*ide 709

好吧,我把一个测试程序放在一起,运行每个方法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); 
}
Run Code Online (Sandbox Code Playgroud)

运行100,000次呼叫的总时间平均超过5次,

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**
Run Code Online (Sandbox Code Playgroud)

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

  • 使用它的任何人都需要记住#include <sys/stat.h>,否则它会尝试使用错误的stat. (99认同)
  • 上述方法都没有检查是否存在,而是检查可访问性.但是,我不知道用一种标准的C或C++方法来检查是否存在. (28认同)
  • 我想对于ifstream方法,你不需要`f.close()`因为f在函数末尾超出范围.所以`return f.good()`可以替换`if`块? (22认同)
  • 您还可以使用/测试即将推出的标准中的http://en.cppreference.com/w/cpp/experimental/fs/exists (10认同)
  • `stat()`似乎检查是否存在. (7认同)
  • 第一个可能只是`return ifstream(filename).good();` (5认同)
  • @EvgenyDanilenko正在读取的文件内容在哪里?该文件正在为read_打开,是的,但这与实际读取文件内容有很大不同. (2认同)
  • 我知道这是特定于编译器的,但是现在(在C ++ 17中)添加了std :: filesystem :: exists,可以将std :: filesystem :: exists基准添加到列表中吗? (2认同)

Vin*_*ent 124

备注:在C++ 14中,只要文件系统TS完成并采用,解决方案就是使用:

std::experimental::filesystem::exists("helloworld.txt");
Run Code Online (Sandbox Code Playgroud)

从C++ 17开始,只有:

std::filesystem::exists("helloworld.txt");
Run Code Online (Sandbox Code Playgroud)

  • 已在[Boost.Filesystem]中找到(http://www.boost.org/doc/libs/1_55_0/libs/filesystem/doc/index.htm) (4认同)
  • 不要忘记`#include &lt;experimental/filesystem&gt;` (4认同)
  • 对于那些在 Linux 上使用 C++17 选项的人,不要忘记将 `stdc++fs` lib 添加到您的 CMakeLists.txt 中。例如:“target_link_libraries(&lt;executable_name&gt; stdc++fs)” (4认同)
  • 我实际上希望它不会是`std :: exists`,这会让人很困惑(想想:存在于像集合一样的STL容器中). (3认同)
  • 同样在Visual Studio 2015中:`#include &lt;experimental / filesystem&gt; bool file_exists(std :: string fn){std :: experimental :: filesystem :: exists(“ helloworld.txt”); }` (2认同)

小智 105

我使用这段代码,到目前为止它对我来说还可以.这并没有使用C++的许多奇特功能:

bool is_file_exist(const char *fileName)
{
    std::ifstream infile(fileName);
    return infile.good();
}
Run Code Online (Sandbox Code Playgroud)

  • @ Mo0gles:`ifstream`析构函数将在退出`is_file_exist`时被调用,它将关闭流. (28认同)
  • 但是,如果文件被其他程序锁定或者无法访问该文件,则可能会失败. (7认同)
  • @Orwellophile` return std :: ifstream(fileName);` (4认同)
  • @emlai 应该是`return static_cast&lt;bool&gt;(std::ifstream(fileName));`。如果没有 static_cast,编译器会抱怨。 (3认同)
  • 你需要关闭流吗? (2认同)
  • 从 C++11 开始,您可以使用 bool 运算符在一行中完成:http://en.cppreference.com/w/cpp/io/basic_ios/operator_bool (2认同)

Jim*_*ter 26

这取决于文件所在的位置.例如,如果它们都应该位于同一目录中,则可以将所有目录条目读入哈希表,然后根据哈希表检查所有名称.在某些系统上,这可能比单独检查每个文件更快.单独检查每个文件的最快方法取决于您的系统...如果您正在编写ANSI C,最快的方法是fopen因为它是唯一的方法(文件可能存在但不可打开,但您可能真的想要打开,如果你需要"做点什么").C++,POSIX,Windows都提供了其他选项.

在我做的时候,让我指出你的问题有些问题.你说你想要最快的方式,并且你有数千个文件,但是你要求函数的代码来测试单个文件(并且该函数仅在C++中有效,而不是C).这通过对解决方案做出假设而与您的要求相矛盾......这是XY问题的一个例子.你还说"在标准的c ++ 11(或)c ++(或)c"......这些都是不同的,这也与你对速度的要求不一致......最快的解决方案是将代码定制到目标系统.问题的不一致性在于您接受了一个答案,该答案提供了依赖于系统且不是标准C或C++的解决方案.


anh*_*ppe 22

对于那些喜欢激励的人:

 boost::filesystem::exists(fileName)
Run Code Online (Sandbox Code Playgroud)

  • 并非高性能应用程序的所有方面都需要优化.例如,读取命令行或配置文件可能很复杂,并且可能不需要速度,尽管应用程序本身可能需要C++的性能优势.在这种情况下避免提升构成车轮改造,在反模式清单上占据重要位置. (29认同)
  • 提升通常非常慢. (5认同)
  • 对于大多数应用程序,文件存在检查不是性能关键 (4认同)
  • @SergeRogatch boost :: filesystem :: exists并不是非常慢.有关详细信息,请参阅基准测试结果 (4认同)
  • “Boost 通常非常慢”——这是错误的,甚至不清楚索赔的范围是什么...... Boost 包含许多不同作者的包,但经过了高质量的审查。“对于大多数应用程序,文件存在检查不是性能关键”——由于检查大量文件,OP 特别要求速度。“如果性能不重要,那么使用 C++ 也没有意义”——另一个错误的评论(和题外话)。大多数软件是在*商店*中编写的,并且是强制选择语言的*系统*的一部分。 (4认同)

Ram*_*tra 20

与PherricOxide建议的相同,但在C中

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


Vik*_*ehr 19

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

这适用于Windows和POSIX兼容系统的跨平台.

  • Mac OSX符合POSIX标准. (3认同)

小智 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.
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你真的要这样做,只需"return(bool)file"而不是使用if/else分支. (17认同)
  • 第二个想法也许你不需要明确地关闭它......我忘了ifstream是一个RAII(资源获取是初始化)......并且当它从析构函数中超出范围时会自我清理......我可以说......这些天我被垃圾收集器语言洗脑了...... (2认同)

rav*_*ang 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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

3

inline bool exist(const std::string& name)
{
    return GetFileAttributes(name.c_str()) != INVALID_FILE_ATTRIBUTES;
}
Run Code Online (Sandbox Code Playgroud)

  • `GetFileAttributes`版本基本上是在Windows中执行它的规范方式. (5认同)

hun*_*tit 7

我需要一个可以检查文件是否存在的快速函数,而 PherricOxide 的答案几乎是我所需要的,只是它没有比较 boost::filesystem::exists 和 open 函数的性能。从基准测试结果我们可以很容易地看出:

  • 使用 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
Run Code Online (Sandbox Code Playgroud)

以下是我的基准代码:

#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();   
Run Code Online (Sandbox Code Playgroud)


par*_*arv 5

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


use*_*302 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;
}
Run Code Online (Sandbox Code Playgroud)


Jay*_*llo 5

您可以使用std::ifstream, 类似is_open, 的功能fail,例如如下代码(cout“open”表示文件存在与否):

在此处输入图片说明

在此处输入图片说明

从这个答案中引用