我想找到最快的方法来检查一个文件是否存在于标准c++ 11, 14, 17,或C。我有成千上万的文件,在对它们做一些事情之前,我需要检查它们是否都存在。在下面的函数中,我可以写什么来代替/* SOMETHING */ ?
inline bool exist(const std::string& name)
{
/* SOMETHING */
}
我想找到最快的方法来检查一个文件是否存在于标准c++ 11, 14, 17,或C。我有成千上万的文件,在对它们做一些事情之前,我需要检查它们是否都存在。在下面的函数中,我可以写什么来代替/* SOMETHING */ ?
inline bool exist(const std::string& name)
{
/* SOMETHING */
}
It depends on where the files reside. For instance, if they are all supposed to be in the same directory, you can read all the directory entries into a hash table and then check all the names against the hash table. This might be faster on some systems than checking each file individually. The fastest way to check each file individually depends on your system ... if you're writing ANSI C, the fastest way is fopen because it's the only way (a file might exist but not be openable, but you probably really want openable if you need to "do something on it"). C++, POSIX, Windows all offer additional options.
While I'm at it, let me point out some problems with your question. You say that you want the fastest way, and that you have thousands of files, but then you ask for the code for a function to test a single file (and that function is only valid in C++, not C). This contradicts your requirements by making an assumption about the solution ... a case of the XY problem. You also say "in standard c++11(or)c++(or)c" ... which are all different, and this also is inconsistent with your requirement for speed ... the fastest solution would involve tailoring the code to the target system. The inconsistency in the question is highlighted by the fact that you accepted an answer that gives solutions that are system-dependent and are not standard C or C++.
我编写了一个测试程序,每个方法都运行了10万次,一半在存在的文件上,一半在不存在的文件上。
#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 | Time |
---|---|
exists_test0 (ifstream) |
0.485s |
exists_test1 (FILE fopen) |
0.302s |
exists_test2 (posix access()) |
0.202s |
exists_test3 (posix stat()) |
0.134s |
stat()函数在我的系统(Linux,用g++编译)上提供了最好的性能,如果您出于某种原因拒绝使用POSIX函数,那么标准的fopen调用是您最好的选择。
我使用这段代码,到目前为止,它工作得很好。它没有使用c++的许多奇特的特性:
bool is_file_exist(const char *fileName)
{
std::ifstream infile(fileName);
return infile.good();
}
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.
}
与PherricOxide的建议相同,但在C中
#include <sys/stat.h>
int exist(const char *name)
{
struct stat buffer;
return (stat (name, &buffer) == 0);
}
备注:在c++ 14中,一旦文件系统TS完成并被采用,解决方案将使用:
std::experimental::filesystem::exists("helloworld.txt");
从c++ 17开始,只有:
std::filesystem::exists("helloworld.txt");
你也可以使用bool b = std::ifstream('filename').good();。如果没有分支指令(比如if),它必须执行得更快,因为它需要被调用数千次。
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;
}
虽然有几种方法可以做到这一点,但对您的问题最有效的解决方案可能是使用fstream的预定义方法之一,例如good()。使用此方法可以检查指定的文件是否存在。
fstream file("file_name.txt");
if (file.good())
{
std::cout << "file is good." << endl;
}
else
{
std::cout << "file isnt good" << endl;
}
我希望这对你有用。
all_of (begin(R), end(R), [](auto&p){ exists(p); })
其中R是你的路径序列,exists()来自未来std或当前boost。如果你自己卷,简单点,
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
}
如果你需要区分一个文件和一个目录,考虑下面这两个都使用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;
}
对于那些喜欢刺激的人:
boost::filesystem::exists(fileName)
或者,自ISO c++ 17开始:
std::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兼容系统的跨平台。
使用MFC可以实现以下功能
CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);
哪里的文件名是一个字符串表示文件,您正在检查的存在
我需要一个快速的函数,可以检查一个文件是否存在,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
下面是我的基准代码:
#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();
在c++ 17中:
#include <experimental/filesystem>
bool is_file_exist(std::string& str) {
namespace fs = std::experimental::filesystem;
fs::path p(str);
return fs::exists(p);
}
只有一种更快的方法来检查文件是否存在,如果你有权限读取它,这种方法是使用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;
}
这里有一个简单的例子!
#include <iostream>
#include <fstream>
using namespace std;
void main(){
SearchFile("test.txt");
}
bool SearchFile(const char *file)
{
ifstream infile(file);
if (!infile.good())
{
// If file is not there
exit(1);
}
}
所有其他答案都侧重于单独检查每个文件,但如果所有文件都在一个目录(文件夹)中,那么读取目录并检查是否存在所需的每个文件名可能会更有效。
即使文件分布在几个目录中,这也可能更有效,这取决于目录与文件的确切比例。一旦您开始接近每个目标文件都在自己的目录中,或者在相同的目录中有许多其他文件,而您不想检查,那么我预计它最终会变得比单独检查每个文件更低效。
A good heuristic: working on a bunch of data you already have is much faster than asking the operating system for any amount of data. System call overhead is huge relative to individual machine instructions. So it is almost always going to be faster to ask the OS "give me the entire list of files in this directory" and then to dig through that list, and slower to ask the OS "give me information on this file", "okay now give me information on this other file", "now give me information on ...", and so on.
每个优秀的C库都以一种有效的方式实现了“遍历目录下的所有文件”api,就像缓冲I/O一样——在内部,它一次从操作系统中读取一个大的目录条目列表,即使这些api看起来像要求操作系统单独读取每个条目。
如果我有这个要求,我会
尽一切可能鼓励设计和使用,这样所有的文件都在一个文件夹里,没有其他文件在那个文件夹里, 将我需要呈现的文件名列表放入内存中的数据结构中,该数据结构具有O(1)或至少O(log(n))次查找和删除次数(就像哈希映射或二叉树), 列出该目录中的文件,并从内存中的“列表”(哈希映射或二叉树)中“检查”(删除)每个文件。
Except depending on the exact use case, maybe instead of deleting entries from a hash map or tree, I would keep track of a "do I have this file?" boolean for each entry, and figure out a data structure that would make it O(1) to ask "do I have every file?". Maybe a binary tree but the struct for each non-leaf node also has a boolean that is a logical-and of the booleans of its leaf nodes. That scales well - after setting a boolean in a leaf node, you just walk up the tree and set each node's "have this?" boolean with the && of its child node's boolean (and you don't need to recurse down those other child nodes, since if you're doing this process consistently every time you go to set one of the leaves to true, they will be set to true if and only if all of their children are.)
不幸的是,在c++ 17之前没有标准的方法来做这件事。
c++ 17得到std::filesystem::directory_iterator。
当然,有一个相应的boost::filesystem::directory_iterator,我认为它可以在旧版本的c++中工作。
最接近标准C方法的是opendir和readdir from direct .h。这是一个标准的C接口,它只是在POSIX中标准化,而不是在C标准本身中。它可以在Mac OS、Linux、所有bsd、其他UNIX/类UNIX系统和任何其他POSIX/SUS系统上开箱即用。对于Windows,有一个direct .h实现,你只需要下载并放入你的include路径。
然而,由于您正在寻找最快的方法,您可能想要超越便携式/标准的东西。
在Linux上,您可以通过使用原始系统调用getdents64手动指定缓冲区大小来优化性能。
在Windows上,经过一些挖掘,看起来为了获得最大的性能,你想要使用FindFirstFileEx与FindExInfoBasic和FIND_FIRST_EX_LARGE_FETCH,当你可以的时候,这是许多开源库,如上面的direct .h Windows似乎没有做到的。但是对于需要处理比前两个Windows版本更老的东西的代码,您不妨只使用直接的FindFirstFile,而不使用额外的标记。
上面的任何一种方法都不包括计划9,您将需要dirread或dirreadall(如果您可以安全地假设您有足够的内存用于整个目录内容,则选择后者)。如果您想要更好地控制缓冲区大小以提高性能,可以使用普通读取或读取并解码目录条目数据——它们是有文档记录的与机器无关的格式,而且我认为提供了辅助函数。
我不知道其他的操作系统。
稍后我可能会用一些测试编辑这个答案。也欢迎其他人编辑测试结果。
检测文件是否存在于windows中。
bool DoesExistFile(std::wstring filePath)
{
bool result = true;
HANDLE fileHandle = CreateFile(
filePath.c_str(),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if ((fileHandle != NULL) && (fileHandle != INVALID_HANDLE_VALUE))
CloseHandle(fileHandle);
else
{
DWORD error = GetLastError();
if ((error == ERROR_FILE_NOT_FOUND) || (error == ERROR_PATH_NOT_FOUND))
result = false;
}
return result;
}
测试文件是否存在的最快和最安全的方法是根本不单独/显式地测试它。也就是说,看看你是否能找到一种方法来取代普通
if(exists(file)) { /* point A */
/* handle existence condition */
return;
}
do_something_with(file); /* point B */
随着
r = do_something_with_unless_exists(file);
if(r == 0)
success;
else if(errno == EEXIST)
/* handle existence condition */
else
/* handle other error */
除了速度更快之外,这还消除了第一个解决方案中固有的竞争条件(特别是“TOC/TOU”),即文件在点A和点B之间存在的可能性。
显然,第二个解决方案假定存在一种原子方法来执行do_something_with_unless_exists操作。通常总会有办法的,但有时你得四处寻找。
创建文件:使用O_CREAT和O_EXCL调用open()。 创建一个纯C文件,如果你有C11:调用fopen()与"wx"。(我昨天才知道这个。) 创建目录:只需调用mkdir(),然后检查errno == EEXIST。 获取锁:任何称职的锁定系统都已经拥有一个原子性的“只要没有其他人拥有就获取锁”原语。
(还有其他的,但这些是我现在能想到的。)
[脚注:在Unix的早期,没有特定的、专用的工具可用于普通进程进行锁定,所以如果你想建立一个互斥锁,这通常是通过创建一个特定的空目录来实现的,因为mkdir系统调用总是能够根据先前的存在或不存在而原子地失败或成功。]
还有一个更简单的方法
#include <fstream>
#include <iostream>
void FileExists(std::string myfile){
std::ifstream file(myfile.c_str());
if (file) {
std::cout << "file exists" << std::endl;
}
else {
std::cout << "file doesn't exist" << std::endl;
}
}
int main() {
FileExists("myfile.txt");
return 0;
}