C++においてファイルの中身を読み込む際にはifstreamを使う。この時にファイルが存在しなかったり不正なファイルを入力したりした時の挙動については少し注意する必要がある。
ifstreamの挙動
正常な場合
例えば、以下のような入力ファイルがあったとして
1 2
それを変数に読み込む場合、以下のようなコードを書く。
int i, j;
std::ifstream fin("input.txt");
fin >> i >> j;
std::cout << "(i,j) = (" << i << "," << j << ")" << std::endl; // => (1,2)
入力ファイルが存在し、適切なフォーマットで書かれているならば何も問題は起きない。
ファイルが存在しない場合
しかし、もし入力ファイルが存在しなかったらどうなるか?
int i, j;
std::ifstream fin("do_not_exist");
fin >> i >> j;
std::cout << "(i,j) = (" << i << "," << j << ")" << std::endl; // => (0,0) (挙動は未定義?)
実はその場合にも実行時例外などは送出されず、プログラムが正常終了してしまう。
ファイルフォーマットが不正な場合
さらに、以下のような不正なフォーマットの場合を考えよう。
1 a
このファイルに対して上記のコードを動かしても実行時例外などは何も発生せず、(1,0)
と表示される。
(ただし、j
の値がどうなるかは未定義かも。私の環境ではこうなったというだけかもしれない。)
チェック方法
このような不正なフォーマットのファイルが入ってきた場合のエラーをどのように検出するべきか?
ifstreamの状態をチェックするには fin.good()
, fin.is_open()
など様々なメソッドがありややこしいが、結論から言えばoperator bool
でチェックするのがベストプラクティスになる。
要するに
std::ifstream fin("....txt");
if( !fin ) {
.... // 何かエラーが起きている
}
ファイルの存在やパーミッションについてはfin.is_open()
でチェックすることができるが、operator bool
はis_open
によるチェックも含んでいる。
https://stackoverflow.com/questions/24097580/ifstreamis-open-vs-ifstreamfail
https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool
例えば、先ほどの例での結果は以下のようになる。
// 正常系の場合
int i, j;
std::ifstream fin("input.txt");
std::cout << static_cast<bool>(fin) << std::endl; // => 1
fin >> i >> j;
std::cout << "(i,j) = (" << i << "," << j << ")" << std::endl; // (1,2)
std::cout << static_cast<bool>(fin) << std::endl; // => 1
// ファイルが存在しない場合
int i, j;
std::ifstream fin("do_not_exist");
std::cout << static_cast<bool>(fin) << std::endl; // => 0
fin >> i >> j;
std::cout << "(i,j) = (" << i << "," << j << ")" << std::endl;
std::cout << static_cast<bool>(fin) << std::endl; // => 0
// 入力の形式が不正な場合
int i, j;
std::ifstream fin("invalid.txt");
std::cout << static_cast<bool>(fin) << std::endl; // => 1
fin >> i >> j;
std::cout << "(i,j) = (" << i << "," << j << ")" << std::endl;
std::cout << static_cast<bool>(fin) << std::endl; // => 0
というわけで、入力ファイルが存在するかどうかをチェックするためにもifstream
でファイルを開いたら最低限次のようなチェックを入れておくことを習慣化しておくとよい。
ifstream fin("my_file.txt");
if (!fin) {
// エラー処理
}