我想找到一种检查标准C ++ 11,C ++或C中是否存在文件的最快方法。我有成千上万个文件,在对它们进行处理之前,我需要检查所有文件是否都存在。我可以写些什么而不是/* SOMETHING */
下面的函数?
inline bool exist(const std::string& name)
{
/* SOMETHING */
}
git push
在进行最初的脏检查后,可能不会费心确保您没有触摸工作树。
我想找到一种检查标准C ++ 11,C ++或C中是否存在文件的最快方法。我有成千上万个文件,在对它们进行处理之前,我需要检查所有文件是否都存在。我可以写些什么而不是/* SOMETHING */
下面的函数?
inline bool exist(const std::string& name)
{
/* SOMETHING */
}
git push
在进行最初的脏检查后,可能不会费心确保您没有触摸工作树。
Answers:
好吧,我整理了一个测试程序,每种方法都运行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函数,则最好的选择是标准调用。
stat()
似乎要检查是否存在。
f.close()
由于f在函数末尾超出范围,您不需要。那么return f.good()
可以更换if
块吗?
备注:在C ++ 14中,一旦文件系统TS完成并被采用,解决方案将使用:
std::experimental::filesystem::exists("helloworld.txt");
从C ++ 17开始,仅:
std::filesystem::exists("helloworld.txt");
std::tr2::sys::exists("helloworld.txt");
std::exists
,那会很令人困惑(想想:像集合一样存在于STL容器中)。
#include <experimental/filesystem> bool file_exists(std::string fn) { std::experimental::filesystem::exists("helloworld.txt"); }
#include <experimental/filesystem>
我使用这段代码,到目前为止对我来说还可以。这没有使用C ++的许多高级功能:
bool is_file_exist(const char *fileName)
{
std::ifstream infile(fileName);
return infile.good();
}
ifstream
析构函数将在退出时被调用,is_file_exist
并将关闭流。
return std::ifstream(fileName);
这取决于文件所在的位置。例如,如果假定它们都在同一个目录中,则可以将所有目录条目读入哈希表,然后对照哈希表检查所有名称。在某些系统上,这可能比分别检查每个文件更快。单独检查每个文件的最快方法取决于您的系统...如果要编写ANSI C,最快的方法是fopen
因为这是唯一的方法(文件可能存在但不可打开,但是如果您确实希望打开文件,需要“做一些事情”)。C ++,POSIX,Windows均提供其他选项。
在讨论过程中,请允许我指出您的问题。您说您想要最快的方法,并且您有成千上万个文件,但是随后您要求提供用于测试单个文件的函数的代码(该函数仅在C ++中有效,而在C中无效)。通过对解决方案进行假设(这是XY问题的情况)来与您的要求相矛盾。您还说了“在标准c ++ 11(or)c ++(or)c中” ...两者都是不同的,这也与您对速度的要求不一致...最快的解决方案将包括针对目标系统。您接受了一个答案,该答案给出的解决方案依赖于系统并且不是标准的C或C ++,这一事实突出表明了问题中的不一致。
对于那些喜欢助推器的人:
boost::filesystem::exists(fileName)
在不使用其他库的情况下,我喜欢使用以下代码片段:
#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兼容系统的跨平台。
unistd.h
也能够包含Mac 。也许第一个#ifdef
应该是特定于Windows的?
与PherricOxide建议的相同,但在C中
#include <sys/stat.h>
int exist(const char *name)
{
struct stat buffer;
return (stat (name, &buffer) == 0);
}
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.
}
close()
没有必要指出。
Windows下的另外3个选项:
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;
}
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;
}
inline bool exist(const std::string& name)
{
return GetFileAttributes(name.c_str()) != INVALID_FILE_ATTRIBUTES;
}
GetFileAttributes
版本基本上是Windows中执行该操作的规范方法。
如果需要区分文件和目录,请考虑以下两个都使用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;
}
我需要一个可以检查文件是否存在的快速函数,而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();
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
没有这个限制。
GetFileAttributes
也限于MAX_PATH。如果您使用绝对路径,unicode并在路径名称前添加特殊的前缀字符串,则文档描述了一种解决方法。我认为我们还是要与Windows特定的响应切线。
GetFileAttributesW
没有限制。
使用MFC可以通过以下方式
CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);
FileName
代表您要检查存在性的文件的字符串在哪里
只有一种更快的方法来检查文件是否存在以及您是否有权读取它,即使用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;
}
尽管有多种方法可以执行此操作,但最有效的解决方案可能是使用fstream的预定义方法之一,例如good()。使用此方法,您可以检查指定的文件是否存在。
fstream file("file_name.txt");
if (file.good())
{
std::cout << "file is good." << endl;
}
else
{
std::cout << "file isnt good" << endl;
}
希望这个对你有帮助。
boost::filesystem
似乎用stat()
。(根据文档。)我认为您无法更快地完成FS调用。快速执行操作的方法是“避免查看数千个文件”。