はじめに
C++で cin
や ifstream
を使ってデータを読み込む際、ユーザーが数値を入力すべきところに文字列を入力するなど、予期せぬ入力によってストリームがエラー状態になることがあります。一度エラー状態になると、そのストリームはそれ以降の入力を全て無視してしまいます。
この問題を解決するには、
- ストリームのエラー状態を検知する。
- エラー状態をリセットする。
- 不正な入力データをストリームから取り除く。
という3つのステップが必要です。この記事では、ストリームのエラー状態を管理するための基本的なメンバ関数と、エラーから回復するための定石的な手法を解説します。
ストリームのエラー状態
ストリームの状態は、内部的に複数のフラグで管理されています。
状態フラグ | メンバ関数 | 意味 |
goodbit | .good() | エラーなしの正常な状態 |
eofbit | .eof() | 入力の終端 (End-of-File) に到達した |
failbit | .fail() | 書式エラーなど、回復可能な論理エラー(例: int にabc を入力) |
badbit | .bad() | 回復不能な深刻なエラー(例: ディスク障害) |
if (cin)
のようにストリームを直接評価すると、.fail()
または.bad()
がtrue
の場合にfalse
と評価されます。
エラーの検知と回復のサンプルコード
このコードは、ユーザーに数値の入力を促し、もし不正な入力(文字列など)があった場合にエラーを検知し、ストリームの状態をリセットして、再度入力を受け付けられるように回復させます。
完成コード
#include <iostream>
#include <limits> // numeric_limits
using namespace std;
int main() {
int input_value;
cout << "整数を入力してください: ";
// >> 演算子で入力を試みる
cin >> input_value;
// 1. ストリームの状態をチェック
// fail()は、failbitまたはbadbitが立っている場合にtrueを返す
if (cin.fail()) {
cout << "不正な入力です。数値ではありません。" << endl;
// 2. clear()で、ストリームのエラーフラグをリセットする
cin.clear();
// 3. ignore()で、入力バッファに残っている不正な入力(と改行)をクリアする
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "\n再度、整数を入力してください: ";
cin >> input_value;
if (cin.fail()) {
cout << "再度入力が失敗したため、プログラムを終了します。" << endl;
return 1;
}
}
cout << "入力された数値は " << input_value << " です。" << endl;
return 0;
}
コードの解説
1. if (cin.fail())
.fail()
メンバ関数は、failbit
またはbadbit
が設定されている場合にtrue
を返します。int
型の変数に文字列を入力しようとすると、cin
は読み込みに失敗し、failbit
が設定されます。これにより、エラーを検知できます。
2. cin.clear()
.clear()
メンバ関数は、ストリームのエラー状態フラグをリセットし、ストリームを正常な状態(goodbit
)に戻します。これを実行しない限り、ストリームはエラー状態のままで、以降の入力操作を一切受け付けません。
3. cin.ignore(...)
.clear()
はエラーフラグをリセットするだけで、入力バッファに残っている不正なデータ(例: ユーザーが入力した"abc"
という文字列)はそのままです。 .ignore()
は、この不要なデータをバッファから捨てるためのものです。
numeric_limits<streamsize>::max()
: 「非常に大きな数」を意味します。最大でこれだけの文字を読み飛ばします。'\n'
: 読み飛ばしを停止する区切り文字です。
この行は、「改行文字 \n
が現れるまで、または非常に多くの文字を読み込むまで、バッファの内容を無視しなさい」という意味になり、現在の行の不正な入力を一掃できます。
まとめ
今回は、C++のストリームにおけるエラーハンドリングの基本を解説しました。
.fail()
で、入力エラーが発生したかを検知する。.clear()
で、ストリームのエラー状態をリセットする。.ignore()
で、入力バッファに残った不正なデータをクリアする。
この「検知 → リセット → クリア」の3ステップが、ユーザーの不正な入力などからプログラムを回復させ、堅牢な入力処理を実装するための定石的な手法です。