問題の処理:1~9までの数字を入力する際、不正入力されたとき再入力をさせる処理
// C++の再入力処理. #include<iostream> using namespace std; int main() { int num; // 1~9が入力されていなければ再入力 cout << "Please enter the numbers(1-9) --> "; while(1) { cin >> num; if(num > 0 && num < 10)break; cout << "Please enter it again.(1-9) -->"; } return 0; }このとき、半角英数字「a」を入力すると.......
$ ./a.out Please enter the numbers(1-9) --> a Please enter it again.(1-9) -->Please enter it again.(1-9) -->Please enter it again.(1-9) -->Please enter it again.(1-9) --> Please enter it again.(1-9) -->Please enter it again.(1-9) -->Please enter it again.(1-9) -->Please enter it again.(1-9) --> (..以下略)と無限ループした。
なん。。だと。。
ループするということは、while文が動いていて、「 cin >> num;」 を何度も通るはず。。そのたびに入力処理が走るはずだが。。。
原因と対策を調べてみた。
原因
まぁどう考えてもcinの仕組みの理解不足だったw当初、英数字はASCIIに従い、「'a'→ 97(0x61)」になると思ってたけど、そうじゃない
なんと型が違うと、エラーフラグが立って、入力値はcinのバッファに残り、再入力を受付けない仕組みになってた!
つまり、「a」を入力したときの流れは
- cin のバッファに「a」を格納
- cin のバッファ「a」をintに変換してnumに代入しようとする
- 型が合わないのでエラー。エラーフラグを立てる。
- numにはとりあえず「0」を入れる
- ループしてwhile文の先頭へ
- 「バッファにまだ入力値がある」または「エラーフラグが立っている」ので、再入力をスルー
- cin のバッファ「a」をnumに変換して代入しようとする
- 型が合わないのでエラー。エラーフラグを立てる。 ・
・
・
はまってたのはコレ
ちなみに、手順4の「numにはとりあえず「0」を入れる」のも曲者で
もし、break条件にnum = 0が含まれてたら、不正値でも再入力ループを抜けてしまう
対策しないと0を入力する条件がある場合に困る
対策
- 無限ループ対策 手順6で再入力させればいいので、
- 「とりあえず「0」を入れる」対策 これもちょうどいいメソッドがある
「cinのバッファを消す」、「エラーフラグを落とす」をすればよい
cinはistreamクラスを継承したオブジェクトなので、その辺のクラスを調べると丁度良いメソッドを見つけた
ループするときにこのメソッドを呼んでやると再入力を受け付けてくれる
メソッド名 | 効果 |
---|---|
clear | エラーフラグを落とす |
ignore | バッファを消す |
メソッド名 | 効果 |
---|---|
good | 変換が成功したか確認する |
このメソッドをbreak文の条件に入れることで、num = 0が不正値の0か、入力値の0かを判断できる
結果
以上より、最終的にこう書けばすべての入力に対して無敵!(修正行を色付にしている)
// C++の再入力処理. #include<iostream> using namespace std; int main() { int num; // 1~9が入力されていなければ再入力 cout << "Please enter the numbers(1-9) --> "; while(1) { cin >> num; // cin.good()が0のときは不正入力 if( (cin.good()!=0) && num > 0 && num < 10) break; cin.clear(); // エラーフラグクリア cin.ignore(256,'\n'); // バッファクリア cout << "Please enter it again.(1-9) -->"; } return 0; }一応、cin.ignore(256,'\n');の意味の説明
第一引数は消すバッファのByte数。256に意味はないけど、これくらいあれば全部消せそう
第二引数はバッファを消す終端文字を指す
C言語と同様に文字列の最後には'\n'が入るため、終端を'\n'で指定している
結果は
$ ./a.out Please enter the numbers(1-9) --> a Please enter it again.(1-9) -->あ Please enter it again.(1-9) -->999999999999 Please enter it again.(1-9) -->abc nnn Please enter it again.(1-9) -->aaaaaaaaaaaaaaaaaa Please enter it again.(1-9) -->くぁwせdrftgyふじこlp Please enter it again.(1-9) -->.. Please enter it again.(1-9) -->1 $完成!!
0 件のコメント:
コメントを投稿