C++のファイルの存在確認の方法について

AI実装検定のご案内

C++でファイルの存在を確認する方法はいくつかあります。

現在のC++では、C++17以降で使える std::filesystem を利用する方法が最も基本的でおすすめです。

ただし、「ファイルが存在するか」を確認したいのか、「ファイルを読み込めるか」を確認したいのかによって、適切な方法は変わります。

たとえば、単純にパスが存在するかを確認したい場合と、通常のファイルだけを確認したい場合では、使う関数が異なります。

また、ファイルを実際に読み込むことが目的なら、存在確認を別に行うよりも、ファイルを開けるかどうかで判断する方が実用的なケースもあります。

目次

C++17以降ならstd::filesystemを使うのがおすすめ

C++17以降では、標準ライブラリに std::filesystem が用意されています。

std::filesystem は、ファイルやディレクトリなどのパスを扱うための機能です。

ファイルの存在確認だけでなく、ディレクトリの確認、ファイルサイズの取得、パスの結合、拡張子の取得など、ファイルシステム関連の処理を標準的に扱えます。

そのため、C++17以降の環境であれば、基本的には std::filesystem を使うのがよいでしょう。

std::filesystemを使うメリット

std::filesystem を使う大きなメリットは、C++標準の機能として用意されている点です。

従来は、LinuxやmacOSでは stat()、WindowsではWindows APIなど、環境ごとに異なる方法を使うことがありました。

しかし、std::filesystem を使えば、OSごとの差をある程度吸収しながら、より統一的な書き方でファイルやディレクトリを扱えます。

また、ファイルだけでなくディレクトリの判定もできるため、「通常ファイルか」「ディレクトリか」「そもそもパスが存在するか」といった条件を分けて確認しやすいのも特徴です。

existsはファイルとディレクトリの両方に反応する

std::filesystem には、パスが存在するかどうかを確認するための exists() があります。

ただし、exists() は「通常のファイルが存在するか」だけを確認するものではありません。

指定したパスが存在していれば、ファイルでもディレクトリでも true になります。

そのため、exists() は「ファイルの存在確認」というより、パスの存在確認として理解した方が正確です。

たとえば、対象が通常ファイルではなくディレクトリだったとしても、そのパスが存在していれば exists() は存在すると判断します。

ファイルだけを確認したい場合はis_regular_fileを使う

通常のファイルだけを確認したい場合は、exists() よりも is_regular_file() を使う方が適切です。

is_regular_file() は、指定したパスが通常ファイルである場合に true を返します。

そのため、ディレクトリや特殊なファイルを通常ファイルとして誤って扱いたくない場合に向いています。

「ファイルが存在するか」を確認したい場面では、多くの場合、この is_regular_file() を使うのが実務的です。

特に、ファイルを読み込む前のチェックや、設定ファイル・ログファイル・CSVファイルなどの存在確認では、単にパスがあるかではなく、通常ファイルとして存在しているかを確認した方が安全です。

ディレクトリの存在確認をしたい場合

ディレクトリが存在するかを確認したい場合は、is_directory() を使います。

is_directory() は、指定したパスがディレクトリである場合に true を返します。

通常ファイルの場合は true になりません。

たとえば、データ保存用のフォルダ、設定ファイルを置くフォルダ、一時ファイルを出力するフォルダなどが存在するかを確認したい場合に使います。

ファイルとディレクトリは分けて判定する

ファイルの存在確認とディレクトリの存在確認は、目的に応じて分けて考える必要があります。

「パスがあれば何でもよい」場合は exists() で問題ありません。

しかし、「通常ファイルでなければならない」場合は is_regular_file() を使います。

「ディレクトリでなければならない」場合は is_directory() を使います。

この違いを理解しておかないと、ディレクトリをファイルとして扱ってしまったり、逆にファイルをディレクトリとして扱ってしまったりする可能性があります。

例外を避けたい場合はerror_codeを使う

std::filesystem の関数は、状況によって例外を投げることがあります。

たとえば、アクセス権がないパスを確認しようとした場合や、パスの形式に問題がある場合などです。

例外を使って処理したい場合はそのままでも構いませんが、例外を避けたい場合は std::error_code を受け取る形式を使うとよいです。

falseの理由は1つではない

ファイルの存在確認で注意したいのは、結果が false だった場合です。

false になったからといって、必ずしも「ファイルが存在しない」とは限りません。

次のような可能性があります。

ファイルが本当に存在しない場合もあります。

しかし、権限がなくて確認できなかった場合や、パスの途中にアクセスできないディレクトリがある場合、I/Oエラーが発生した場合などもあります。

そのため、厳密な処理が必要なプログラムでは、単に true か false だけを見るのではなく、エラー情報も確認した方が安全です。

簡単な処理ならエラーをまとめてfalse扱いしてもよい

一方で、簡単なツールや小規模なプログラムでは、「存在しない、または確認できない場合は false でよい」と割り切ることもあります。

たとえば、任意の設定ファイルがあれば読み込み、なければデフォルト設定を使うような処理では、細かいエラー理由を区別しなくても問題ないことがあります。

ただし、業務システムやログ出力が必要なプログラムでは、「存在しない」のか「権限エラーで確認できない」のかを区別できた方が、原因調査がしやすくなります。

std::ifstreamで確認する方法

C++17より前の環境では、std::ifstream を使ってファイルの存在を確認する方法もよく使われます。

std::ifstream は、ファイルを読み込み用に開くための機能です。

ファイルを開ければ成功、開けなければ失敗として扱えます。

ただし、この方法は厳密には「ファイルが存在するか」を確認しているのではなく、ファイルを読み込み用に開けるかどうかを確認している点に注意が必要です。

ifstreamは存在確認ではなく読み込み確認に近い

std::ifstream で確認できるのは、主に「読み込み用に開けるかどうか」です。

そのため、ファイルが存在しない場合はもちろん失敗します。

しかし、ファイルが存在していても、読み取り権限がなければ失敗します。

また、指定したパスが通常ファイルではない場合にも、期待どおりに開けないことがあります。

つまり、std::ifstream で失敗した場合、それだけで「ファイルが存在しない」と断定するのは正確ではありません。

読み込みが目的ならifstreamで開いて確認するのが実用的

ファイルを読み込むことが目的であれば、事前に存在確認を行うよりも、実際にファイルを開いて、開けたかどうかを確認する方が実用的です。

なぜなら、存在確認をした直後に、別の処理によってファイルが削除されたり、移動されたり、権限が変更されたりする可能性があるからです。

このような問題を考えると、読み込み処理では「存在するか」を先に確認するよりも、「開けるか」を確認して、開けなかった場合の処理を書く方が安全です。

statを使う方法

C++では、C言語由来の stat() を使ってファイルの存在を確認する方法もあります。

stat() は、指定したパスのファイル情報を取得するための関数です。

情報の取得に成功すれば、そのパスが存在すると判断できます。

ただし、stat() はC++標準の機能ではありません。

LinuxやmacOSなどのPOSIX系環境でよく使われる方法です。

statは環境依存の方法

stat() は、POSIX系の環境では一般的に使えます。

一方で、Windowsでは似た関数や互換関数が用意されている場合がありますが、環境によって書き方や挙動が変わることがあります。

そのため、移植性を重視するC++のプログラムでは、stat() よりも std::filesystem を使う方がおすすめです。

特に、Windows、Linux、macOSの複数環境で動かす可能性がある場合は、標準ライブラリであるstd::filesystem を優先した方がよいでしょう。

Windows APIを使う方法

Windows専用のプログラムでは、Windows APIを使ってファイルの存在確認を行うこともできます。

代表的なものに GetFileAttributes() があります。

この関数は、指定したファイルやディレクトリの属性を取得するためのものです。

属性の取得に成功すれば、そのパスは存在していると判断できます。

さらに、取得した属性を確認することで、対象がディレクトリかどうかも判定できます。

Windows専用なら有効だが移植性は低い

Windows APIを使う方法は、Windows環境に特化したプログラムでは有効です。

ただし、LinuxやmacOSではそのまま使えません。

そのため、Windows専用アプリケーションであれば選択肢になりますが、複数OSで動作させたい場合は std::filesystem を使う方が適しています。

また、日本語パスやUnicodeパスを扱う場合は、ANSI版よりもUnicode版のAPIを意識した方がよい場面があります。

相対パスと絶対パスに注意する

ファイルの存在確認でよくあるミスが、相対パスの基準を勘違いすることです。

相対パスでファイル名を指定した場合、その基準になるのはソースコードのある場所ではありません。

基本的には、プログラム実行時のカレントディレクトリが基準になります。

そのため、ソースコードと同じフォルダにファイルを置いているつもりでも、実行時のカレントディレクトリが別の場所であれば、ファイルが見つからないことがあります。

ファイルが見つからない場合はカレントディレクトリを確認する

ファイルが存在するはずなのに見つからない場合は、まずカレントディレクトリを確認するとよいです。

特に、Visual Studio、CLion、VS Code、Makefile、CMakeなどを使っている場合、実行時の作業ディレクトリが自分の想定と違っていることがあります。

テスト実行時やデバッグ実行時も、通常実行時とはカレントディレクトリが異なる場合があります。

ファイルの存在確認がうまくいかないときは、パスの書き方だけでなく、プログラムがどこを基準にファイルを探しているのかを確認することが大切です。

日本語パスや文字コードにも注意する

Windows環境で日本語を含むファイル名やフォルダ名を扱う場合、文字コードの問題が起きることがあります。

C++17以降では std::filesystem::path を使うことで、パスを扱いやすくなっています。

ただし、コンパイラの設定、ソースファイルの文字コード、実行環境、C++のバージョンによって挙動に差が出ることがあります。

C++20以降ではu8文字列に注意する

C++20以降では、u8 文字列リテラルの型が変わったため、C++17以前と同じ感覚で書くとコンパイルエラーになる場合があります。

そのため、初心者向けの説明では、まず英数字だけのパスで基本を理解し、そのあとで日本語パスやUnicodeパスの扱いを学ぶ方がよいでしょう。

実務で日本語パスを扱う場合は、std::filesystem::path を使いつつ、開発環境の文字コード設定も確認することが重要です。

存在確認後にファイルが消える可能性がある

ファイルの存在確認では、確認した瞬間にはファイルが存在していても、その直後にファイルが削除されたり、移動されたりする可能性があります。

これは、複数のプロセスやスレッドが同じファイルを扱う環境では特に重要です。

たとえば、ある処理がファイルの存在を確認したあと、別の処理がそのファイルを削除するかもしれません。

その場合、存在確認では成功していても、その後の読み込み処理では失敗します。

存在確認だけで安全とは限らない

存在確認は便利ですが、それだけで後続のファイル操作が必ず成功するわけではありません。

ファイルを読み込む場合は、存在確認だけでなく、実際に開けなかった場合の処理を必ず用意しておくべきです。

また、ファイルを書き込む場合も同様です。

存在確認の結果に頼りすぎると、タイミングによって予期しないエラーが発生することがあります。

実際の処理では失敗を前提にする

実務では、ファイル操作は失敗する可能性があるものとして設計することが大切です。

ファイルが存在しない場合、権限がない場合、別のプロセスに使用されている場合、ディスクの問題がある場合など、失敗する理由はさまざまです。

そのため、ファイル存在確認はあくまで事前チェックの一つと考え、実際の読み込みや書き込み処理でもエラー処理を行う必要があります。

目的別の使い分け

C++でファイルの存在確認を行う場合は、目的に応じて方法を使い分けることが大切です。

通常ファイルの存在を確認したい場合

通常のファイルだけを確認したい場合は、C++17以降なら std::filesystem::is_regular_file() を使うのがおすすめです。

これは、ディレクトリではなく通常ファイルであることを確認できるため、設定ファイルやデータファイルなどの確認に向いています。

パスが存在するかだけ確認したい場合

ファイルでもディレクトリでもよく、単にパスが存在するかを確認したい場合は、std::filesystem::exists() を使います。

ただし、exists() はディレクトリにも true を返すため、通常ファイルだけを確認したい場合には向いていません。

ディレクトリの存在を確認したい場合

ディレクトリかどうかを確認したい場合は、std::filesystem::is_directory() を使います。

保存先フォルダや作業用ディレクトリが存在するかを確認する場合に適しています。

ファイルを読み込みたい場合

ファイルを読み込むことが目的であれば、std::ifstream で実際に開けるかどうかを確認するのが実用的です。

この方法では、ファイルの存在だけでなく、読み取り可能かどうかも含めて確認できます。

ただし、失敗した場合の理由は「存在しない」だけとは限らないため、必要に応じてエラー処理を行う必要があります。

C++17以前の環境で確認したい場合

C++17以前の環境では、std::filesystem が使えない場合があります。

その場合は、std::ifstream を使って開けるか確認する方法や、POSIX系環境であれば stat() を使う方法があります。

ただし、新しく書くコードでC++17以降を使えるなら、基本的には std::filesystem を選ぶのがよいでしょう。

方法ごとの特徴

C++でファイルの存在確認をする方法には、それぞれ特徴があります。

std::filesystem::is_regular_file() は、通常ファイルだけを確認したい場合に向いています。

std::filesystem::exists() は、ファイルやディレクトリを問わず、パスが存在するかを確認したい場合に使います。

std::filesystem::is_directory() は、ディレクトリの存在確認に使います。

std::ifstream は、ファイルを読み込み用に開けるかどうかを確認する場合に向いています。

stat() は、POSIX系環境で使われる環境依存の方法です。

Windows APIは、Windows専用のプログラムで有効ですが、移植性は低くなります。

初心者にはstd::filesystemが分かりやすい

これからC++でファイルの存在確認を学ぶなら、まずは std::filesystem を理解するのがおすすめです。

理由は、標準ライブラリとして用意されており、ファイルとディレクトリの判定を明確に分けられるからです。

古い方法やOS依存の方法を先に覚えるよりも、まずは標準的な書き方を身につけた方が、実務でも応用しやすくなります。

実務でおすすめの考え方

実務では、単に「存在するか」だけでなく、その後に何をしたいのかを考えて方法を選ぶことが重要です。

ファイルを読みたいのか、ファイルがあることだけを確認したいのか、ディレクトリを確認したいのかによって、使うべき方法は変わります。

ファイルとして存在するか確認するならis_regular_file

通常ファイルとして存在するかを確認したい場合は、is_regular_file() を使うのが適切です。

この方法なら、ディレクトリをファイルと誤認することを避けられます。

読み込むならifstreamで開けるか確認する

読み込み処理が目的なら、存在確認をしたあとに開くのではなく、最初から開けるかどうかを確認する方が実用的です。

存在確認と読み込み処理の間にはタイムラグがあるため、その間にファイルの状態が変わる可能性があります。

そのため、読み込み処理では、ファイルを開く処理そのものに対して失敗時の対応を書くことが大切です。

エラー理由を区別したいならerror_codeを確認する

ファイルが存在しない場合と、権限エラーで確認できない場合を区別したいなら、std::error_code を使ってエラー内容を確認するとよいです。

簡単な処理では false だけで十分なこともありますが、業務システムやログが必要な処理では、エラー理由を記録しておくとトラブル対応がしやすくなります。

まとめ

C++でファイルの存在確認をする場合、C++17以降であれば std::filesystem を使うのが基本です。

ただし、std::filesystem::exists() は通常ファイルだけでなく、ディレクトリにも true を返します。

そのため、通常ファイルだけを確認したい場合は std::filesystem::is_regular_file() を使うのが適切です。

ディレクトリの存在を確認したい場合は std::filesystem::is_directory() を使います。

ファイルでもディレクトリでもよく、パスが存在するかだけを確認したい場合は std::filesystem::exists() を使います。

また、ファイルを読み込むことが目的なら、存在確認を別に行うよりも、std::ifstream で実際に開けるかどうかを確認する方が実用的です。

C++17以前の環境では、std::ifstreamstat() を使う方法もありますが、現在のC++では std::filesystem を優先するのがよいでしょう。

最終的には、次のように考えると分かりやすいです。

  • 通常ファイルとして存在するか確認したいなら、is_regular_file()
  • パスが存在するかだけ確認したいなら、exists()
  • ディレクトリか確認したいなら、is_directory()
  • 読み込みたいなら、ifstream で開けるか確認する。

このように目的ごとに使い分けることで、C++のファイル存在確認をより正確で安全に行えます。

以上、C++のファイルの存在確認の方法についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次