我想找到最快的方法来检查标准 C++11、14、17 或 C 中是否存在文件。我有数千个文件,在对它们进行操作之前,我需要检查它们是否都存在。我可以写什么而不是/* SOMETHING */
在以下函数中?
inline bool exist(const std::string& name)
{
/* SOMETHING */
}
好吧,我拼凑了一个测试程序,将这些方法中的每一个运行了 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);
}
运行 100,000 次调用的总时间结果平均超过 5 次运行,
方法 | 时间 |
---|---|
exists_test0 (如果流) |
0.485s |
exists_test1 (文件打开) |
0.302s |
exists_test2 (posix 访问()) |
0.202s |
exists_test3 (posix stat()) |
0.134s |
该stat()
函数在我的系统(Linux,用 编译)上提供了最佳性能,如果您出于某种原因拒绝使用 POSIX 函数,则最好g++
使用标准调用。fopen
备注:在 C++14 中,一旦文件系统 TS完成并采用,解决方案将是使用:
std::experimental::filesystem::exists("helloworld.txt");
从 C++17 开始,只有:
std::filesystem::exists("helloworld.txt");
我使用这段代码,到目前为止它对我来说还可以。这并没有使用 C++ 的许多花哨功能:
bool is_file_exist(const char *fileName)
{
std::ifstream infile(fileName);
return infile.good();
}
对于那些喜欢提升的人:
boost::filesystem::exists(fileName)
或者,从 ISO C++17 开始:
std::filesystem::exists(fileName)
这取决于文件所在的位置。例如,如果它们都应该在同一个目录中,您可以将所有目录条目读入哈希表,然后根据哈希表检查所有名称。在某些系统上,这可能比单独检查每个文件更快。单独检查每个文件的最快方法取决于您的系统……如果您正在编写 ANSI C,最快的方法是fopen
因为它是唯一的方法(文件可能存在但不可打开,但您可能真的希望可打开,如果您需要“做点什么”)。C++、POSIX、Windows 都提供了额外的选项。
当我在这里时,让我指出你的问题的一些问题。您说您想要最快的方法,并且您有数千个文件,但随后您要求提供用于测试单个文件的函数的代码(并且该函数仅在 C++ 中有效,而不是 C)。这与您的要求相矛盾,因为它对解决方案进行了假设…… XY 问题的一个案例。您还说“在标准 c++11(or)c++(or)c 中”……它们都是不同的,这也与您对速度的要求不一致……最快的解决方案是根据目标系统。问题中的不一致通过您接受的答案突出显示,该答案提供了与系统相关且不是标准 C 或 C++ 的解决方案。
在不使用其他库的情况下,我喜欢使用以下代码片段:
#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 兼容系统的跨平台。
与 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.
}
我需要一个可以检查文件是否存在的快速函数,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();
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;
}
你也可以这样做bool b = std::ifstream('filename').good();
。如果没有分支指令(如 if),它必须执行得更快,因为它需要被调用数千次。
如果您需要区分文件和目录,请考虑以下两者都使用 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;
}
所有其他答案都侧重于单独检查每个文件,但如果文件都在一个目录(文件夹)中,那么仅读取目录并检查您想要的每个文件名是否存在可能会更有效。
即使文件分布在多个目录中,这甚至可能更有效,这取决于目录与文件的确切比例。一旦您开始接近位于其自己目录中的每个目标文件,或者在同一目录中有许多您不想检查的其他文件,那么我希望它最终会变得效率降低而不是单独检查每个文件。
一个很好的启发式方法:处理你已经拥有的一堆数据比向操作系统询问任何数量的数据要快得多。系统调用开销相对于单个机器指令来说是巨大的。因此,询问操作系统“给我这个目录中的整个文件列表”然后挖掘该列表几乎总是会更快,而询问操作系统“给我关于这个文件的信息”、“好的现在给我关于这个其他文件的信息”、“现在给我关于……的信息”等等。
每个好的 C 库都以一种有效的方式实现其“遍历目录中的所有文件”API,就像缓冲 I/O 一样 - 在内部它一次从操作系统读取目录条目的大列表,即使 API 看起来像分别向操作系统询问每个条目。
所以如果我有这个要求,我会
除了取决于确切的用例,也许不是从哈希映射或树中删除条目,我会跟踪“我有这个文件吗?” 每个条目的布尔值,并找出一个数据结构,使 O(1) 询问“我有每个文件吗?”。也许是一棵二叉树,但每个非叶节点的结构也有一个布尔值,它是其叶节点的布尔值的逻辑与。这可以很好地扩展 - 在叶节点中设置布尔值之后,您只需沿着树走并设置每个节点的“有这个吗?” boolean&&
及其子节点的 boolean (并且您不需要递归那些其他子节点,因为如果您每次将其中一个叶子设置为 true 时都始终如一地执行此过程,它们将被设置为当且仅当他们的所有孩子都是真实的。)
可悲的是,在 C++17 之前没有标准的方法来做到这一点。
C++17 得到std::filesystem::directory_iterator
.
当然有一个对应的boost::filesystem::directory_iterator
,我认为它可以在旧版本的 C++ 中工作。
最接近标准 C 方式的是opendir
and readdir
from dirent.h
。那是一个标准的 C 接口,它只是在 POSIX 中标准化,而不是在 C 标准本身中。它在 Mac OS、Linux、所有 BSD、其他 UNIX/类 UNIX 系统和任何其他 POSIX/SUS 系统上开箱即用。对于 Windows,您只需下载一个dirent.h
实现并将其放入包含路径中即可。
但是,由于您正在寻找最快的方法,您可能希望超越便携式/标准的东西。
在 Linux 上,您可以通过使用原始系统调用手动指定缓冲区大小来优化性能getdents64
。
在 Windows 上,经过一番挖掘,它看起来像你想要使用的最大性能FindFirstFileEx
,FindExInfoBasic
并且FIND_FIRST_EX_LARGE_FETCH
在你可以使用的情况下,很多像上面的 Windows 开源库dirent.h
似乎都没有做到这一点。但是对于需要使用比最后几个 Windows 版本更旧的东西的代码,您不妨只使用FindFirstFile
没有额外标志的简单代码。
计划 9 不会被上述任何内容所涵盖,您将需要dirread
or dirreadall
(如果您可以安全地假设您有足够的内存用于整个目录内容,则后者)。如果您想更好地控制缓冲区大小以提高性能,请使用纯文本read
或read
解码目录条目数据 - 它们采用与机器无关的文档格式,我认为提供了辅助函数。
我不知道任何其他操作系统。
稍后我可能会通过一些测试来编辑这个答案。欢迎其他人在测试结果中进行编辑。
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
}
测试文件存在的最快和最安全的方法是根本不单独/显式测试它。也就是看能不能找到办法代替普通的
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
。通常有一种方法,但有时你必须四处寻找它。
创建文件:open()
使用O_CREAT
和调用O_EXCL
。
如果您有 C11,则在纯 C 中创建文件:fopen()
使用"wx"
. (我昨天才知道这个。)
制作目录:只需调用mkdir()
并稍后检查errno == EEXIST
。
获取锁:任何有价值的锁定系统都已经有一个原子的获取锁,因为它没有任何其他人拥有其他人拥有它的原语。
(还有其他的,但我现在能想到的就这些了。)
[脚注:在 Unix 的早期,没有特定的专用设施可供普通进程使用来进行锁定,所以如果你想设置互斥锁,这通常通过创建某个空目录来实现,因为mkdir
系统调用总是有能力根据先前的存在或不存在原子地失败或成功。]
在 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);
}
好吧,还有更简单的方法
#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;
}
使用 MFC 可以通过以下方式
CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);
FileName
代表您正在检查是否存在的文件的字符串在哪里
这是一个简单的例子!
#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);
}
}
只有一种更快的方法来检查文件是否存在以及您是否有权读取它使用 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;
}
检测文件是否存在于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;
}
尽管有几种方法可以做到这一点,但对您的问题最有效的解决方案可能是使用 fstream 的预定义方法之一,例如good()。使用此方法,您可以检查您指定的文件是否存在。
fstream file("file_name.txt");
if (file.good())
{
std::cout << "file is good." << endl;
}
else
{
std::cout << "file isnt good" << endl;
}
希望这个对你有帮助。