ファイルが標準の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);
}
5回の実行で平均して100,000回の呼び出しを実行した合計時間の結果。
方法 | 時間 |
---|---|
exists_test0 (ifstream) |
0.485秒 |
exists_test1 (ファイルfopen) |
0.302秒 |
exists_test2 (posix access()) |
0.202秒 |
exists_test3 (posix stat()) |
0.134秒 |
この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();
}
ブーストが好きな人のために:
boost::filesystem::exists(fileName)
または、ISO C ++ 17以降:
std::filesystem::exists(fileName)
それはファイルがどこにあるかに依存します。たとえば、それらがすべて同じディレクトリにあると想定されている場合は、すべてのディレクトリエントリをハッシュテーブルに読み込んでから、すべての名前をハッシュテーブルと照合することができます。一部のシステムでは、各ファイルを個別にチェックするよりも高速になる場合があります。各ファイルを個別にチェックする最速の方法は、システムによって異なります... ANSI Cを作成している場合、最速の方法はfopen
それが唯一の方法であるためです(ファイルは存在する可能性がありますが、開くことはできませんが、 「それに何かをする」必要があります)。C ++、POSIX、Windowsはすべて追加のオプションを提供します。
私がそれに取り組んでいる間、あなたの質問に関するいくつかの問題を指摘させてください。最速の方法が必要で、何千ものファイルがあると言いますが、次に、単一のファイルをテストする関数のコードを要求します(その関数は、CではなくC ++でのみ有効です)。これは、解についての仮定を立てることによって要件と矛盾します... XY問題の場合。また、「標準のc ++ 11(または)c ++(または)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 :: exits関数のパフォーマンスは、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();
ウィンドウの下の別の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のように)、何千回も呼び出す必要があるため、より高速に実行する必要があります。
ファイルとディレクトリを区別する必要がある場合は、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;
}
他のすべての回答は、すべてのファイルを個別にチェックすることに焦点を当てていますが、ファイルがすべて1つのディレクトリ(フォルダ)にある場合は、ディレクトリを読み取って、必要なすべてのファイル名の存在をチェックする方がはるかに効率的です。
これは、ファイルが複数のディレクトリに分散している場合でも、ディレクトリとファイルの正確な比率に応じて、さらに効率的になる可能性があります。独自のディレクトリにある各ターゲットファイルに近づき始めたら、または同じディレクトリにチェックしたくない他のファイルがたくさんあると、最終的には効率が低下することになります。各ファイルを個別にチェックするよりも。
優れたヒューリスティック:すでに持っている大量のデータを処理する方が、オペレーティングシステムに任意の量のデータを要求するよりもはるかに高速です。システムコールのオーバーヘッドは、個々のマシン命令に比べて非常に大きくなります。したがって、ほとんどの場合、OSに「このディレクトリ内のファイルのリスト全体を教えてください」と尋ねてからそのリストを調べ、OSに「このファイルに関する情報を教えてください」と尋ねる方が遅くなります。この他のファイルに関する情報を教えてください」、「...に関する情報を教えてください」など。
すべての優れたCライブラリは、バッファリングされたI / Oと同じように、効率的な方法で「ディレクトリ内のすべてのファイルを反復処理」APIを実装します。APIは次のように見えますが、内部的にはOSからディレクトリエントリの大きなリストを一度に読み取ります。 OSに各エントリを個別に要求します。
したがって、この要件がある場合は、
正確なユースケースに依存する場合を除いて、ハッシュマップまたはツリーからエントリを削除する代わりに、「このファイルはありますか?」を追跡します。各エントリのブール値を計算し、O(1)が「すべてのファイルを持っていますか?」と尋ねるデータ構造を見つけます。おそらく二分木ですが、各非リーフノードの構造体にも論理的なブール値があります-そしてそのリーフノードのブール値です。これは適切にスケーリングされます。リーフノードにブール値を設定した後、ツリーを上って各ノードの「これを持っていますか?」を設定するだけです。子ノードのブール値を持つ&&
ブール値(そして、他の子ノードを再帰的に実行する必要はありません。これは、リーフの1つをtrueに設定するたびにこのプロセスを一貫して実行している場合、それらは次のように設定されるためです。すべての子供がそうである場合にのみ真です。)
悲しいことに、C++17までそれを行う標準的な方法はありません。
C++17が取得しstd::filesystem::directory_iterator
ました。
もちろんboost::filesystem::directory_iterator
、C++の古いバージョンで動作すると私が推測する対応するものがあります。
標準のCウェイに最も近いのはopendir
、readdir
からdirent.h
です。これは標準のCインターフェースであり、POSIXで標準化されているだけで、C標準自体では標準化されていません。Mac OS、Linux、すべてのBSD、その他のUNIX / UNIXライクなシステム、およびその他のPOSIX/SUSシステムですぐに使用できます。Windowsの場合、ダウンロードしてインクルードパスにドロップするだけのdirent.h
実装があります。
ただし、最速の方法を探しているので、ポータブル/標準的なものを超えて見たいと思うかもしれません。
Linuxでは、rawシステムコールを使用してバッファサイズを手動で指定することにより、パフォーマンスを最適化できる場合がありますgetdents64
。
Windowsでは、少し掘り下げてみると、使用したい最大のパフォーマンスが得られるように見えますが、上記のWindows用のオープンソースライブラリの多くはそうではないようです。ただし、最後の2つのWindowsバージョンよりも古いバージョンで動作する必要があるコードの場合は、余分なフラグを付けずに単純なものを使用することもできます。FindFirstFileEx
FindExInfoBasic
FIND_FIRST_EX_LARGE_FETCH
dirent.h
FindFirstFile
プラン9は上記のいずれにも含まれず、必要になりますdirread
(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
}
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);
}
ファイルの存在をテストする最も速くて安全な方法は、ファイルを個別に/明示的にテストしないことです。つまり、通常のものを置き換える方法を見つけることができるかどうかを確認してください
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の間に存在する可能性がなくなります。
明らかに、2番目の解決策は、操作を実行するためのアトミックな方法の存在を前提としていdo_something_with_unless_exists
ます。多くの場合、方法はありますが、場合によってはそれを探し回る必要があります。
open()
ファイルの作成:とでO_CREAT
呼び出しますO_EXCL
。
C11を使用している場合は、純粋なCでファイルを作成します。で呼び出しfopen()
ます"wx"
。(私は昨日これについて知ったばかりです。)
ディレクトリを作成する:後で電話mkdir()
して確認するだけです。errno == EEXIST
ロックの取得:その塩に値するロックシステムには、すでにアトミックな取得-ロック-アズ-ロング-アズ-ノーバディ-エルス-ハズ-イットプリミティブがあります。
(他にもありますが、それらは私が今考えることができるものです。)
[脚注:Unixの初期には、ロックを実行するために通常のプロセスで利用できる特定の専用機能はありませんでした。したがって、ミューテックスを設定する場合、これは通常、mkdir
syscall以降、特定の空のディレクトリを作成することによって実装されていました。アトミックに、以前の存在または非存在に基づいて失敗または成功する能力を常に持っていました。]
さらに簡単な方法があります
#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
存在を確認しているファイルを表す文字列はどこにありますか
ファイルが存在するかどうかを確認するためのより高速な方法は1つだけであり、ファイルを読み取る権限がある場合は、C言語を使用する方法がより高速であり、C++のどのバージョンでも使用できます。
解決策:Cには、エラーのタイプを認識するために使用できる数値を含むerrnoと呼ばれる外部(グローバル)整数変数を持つライブラリerrno.hがあります。
#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);
}
}
ファイルが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;
}
これを行うにはいくつかの方法がありますが、問題に対する最も効率的な解決策は、おそらくgood()などのfstreamの事前定義されたメソッドの1つを使用することです。この方法では、指定したファイルが存在するかどうかを確認できます。
fstream file("file_name.txt");
if (file.good())
{
std::cout << "file is good." << endl;
}
else
{
std::cout << "file isnt good" << endl;
}
これがお役に立てば幸いです。