ファイルが標準のC++ 11、C++、またはCに存在するかどうかを確認する最も早い方法を見つけたいと思います。次の関数で/* SOMETHING */
の代わりに何を書くことができますか?
inline bool exist(const std::string& name)
{
/* SOMETHING */
}
それで、私はこれらの方法のそれぞれを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);
}
10回のコールを実行するための合計時間の結果は、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**
stat()
関数は私のシステム(Linux、g++
でコンパイルされたもの)で最高のパフォーマンスを提供し、何らかの理由でPOSIX関数の使用を拒否した場合は標準の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();
}
ファイルが存在する場所によって異なります。たとえば、それらがすべて同じディレクトリにあると想定される場合は、すべてのディレクトリエントリをハッシュテーブルに読み込んでから、ハッシュテーブルに対してすべての名前を確認できます。この たぶん いくつかのシステムでは各ファイルを個別にチェックするより速いでしょう。それぞれのファイルを個別にチェックする最も早い方法はあなたのシステムに依存します…ANSI Cを書いているなら、最速の方法はfopen
です。あなたは「その上で何かをする」必要があります。 C++、POSIX、Windowsはすべて追加オプションを提供します。
私がそれにたどり着いている間、あなたの質問に関するいくつかの問題を指摘させてください。あなたはあなたが最も速い方法が欲しい、そしてあなたがあなたが何千ものファイルを持っていると言うけれども、それからあなたは一つのファイルをテストするための関数のためのコードを求めるこれは解について仮定をすることによってあなたの要求と矛盾します... XY問題の場合 。また、「標準のC++ 11(または)C++(または)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準拠のシステムのためのクロスプラットフォームで動作します。
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.
}
ウィンドウズの下の他の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;
}
ファイルとディレクトリを区別する必要がある場合は、PherricOxideによって実証されているように、どちらも最も速い標準的なツールであるstatを使用する次の点を考慮してください。
#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;
}
bool b = std::ifstream('filename').good();
もできます。分岐命令がない場合(ifのように)、何千回も呼び出される必要があるため、より高速に実行する必要があります。
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
}
ファイルが存在するかどうかをチェックできる高速な関数が必要です。PherricOxideの答えは、boost :: filesystem :: existsとopen関数のパフォーマンスを比較しないことを除けば、ほとんど私が必要とするものです。ベンチマーク結果から、次のことが簡単にわかります。
Stat関数を使用するのがファイルが存在するかどうかを確認する最も速い方法です。私の結果はPherricOxideの答えと一致していることに注意してください。
Boost :: filesystem :: exists関数の性能はstat関数の性能に非常に近く、また移植可能です。あなたのコードからブーストライブラリにアクセスできるのであれば、このソリューションをお勧めします。
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();
例えば、以下のコードのように、std::ifstream
、is_open
、fail
のような関数を使用することができます(cout "open"はファイルが存在するかどうかを意味します)。
これから引用する 答え
MFCを使って以下のことが可能です
CFileStatus FileStatus;
BOOL bFileExists = CFile::GetStatus(FileName,FileStatus);
FileName
は、存在を確認しているファイルを表す文字列です。