web-dev-qa-db-ja.com

入力を読み込んだ後にcin.clear()とcin.ignore()を呼び出すのはなぜですか?

Google Code UniversityのC++チュートリアル 以前はこのコードが使用されていました。

// Description: Illustrate the use of cin to get input
// and how to recover from errors.

#include <iostream>
using namespace std;

int main()
{
  int input_var = 0;
  // Enter the do while loop and stay there until either
  // a non-numeric is entered, or -1 is entered.  Note that
  // cin will accept any integer, 4, 40, 400, etc.
  do {
    cout << "Enter a number (-1 = quit): ";
    // The following line accepts input from the keyboard into
    // variable input_var.
    // cin returns false if an input operation fails, that is, if
    // something other than an int (the type of input_var) is entered.
    if (!(cin >> input_var)) {
      cout << "Please enter numbers only." << endl;
      cin.clear();
      cin.ignore(10000,'\n');
    }
    if (input_var != -1) {
      cout << "You entered " << input_var << endl;
    }
  }
  while (input_var != -1);
  cout << "All done." << endl;

  return 0;
}

cin.clear()cin.ignore()の重要性は何ですか? 10000および\nパラメータが必要なのはなぜですか?

71
JacKeown

cin.clear()cinのエラーフラグをクリアし(将来のI/O操作が正常に機能するように)、その後cin.ignore(10000, '\n')は次の改行にスキップします(同じ行の他のすべてを無視します)別の解析エラーが発生しないように、非数値として)。最大で10000文字しかスキップされないため、コードはユーザーが非常に長く無効な行を入力しないことを想定しています。

87

あなたが入力します

if (!(cin >> input_var))

cinから入力を取得するときにエラーが発生した場合のステートメント。エラーが発生した場合、エラーフラグが設定され、今後入力を取得しようとしても失敗します。それがあなたが必要な理由です

cin.clear();

エラーフラグを削除します。また、失敗した入力は、何らかのバッファーであると想定されるものに含まれます。入力を再度取得しようとすると、バッファ内の同じ入力が読み取られ、再び失敗します。それがあなたが必要な理由です

cin.ignore(10000,'\n');

バッファーから10000文字を取り出しますが、改行(\ n)が検出されると停止します。 10000は一般的な大きな値です。

32
flight

使用する理由:

1)cin.ignore

2)cin.clear

単に:

1)ストリームに不要な値を無視(抽出および破棄)する

2)ストリームの内部状態をクリアする。 cin.clearを使用すると、内部状態が再びgoodbitに設定されます。つまり、「エラー」はありません。

ロングバージョン:

「ストリーム」(cin)に何かが置かれている場合は、そこから取得する必要があります。 「取得」とは、ストリームから「使用済み」、「削除済み」、「抽出済み」を意味します。ストリームにはフローがあります。データは小川のように流れています。あなたは単に水の流れを止めることはできません;)

例を見てください:

string name; //line 1
cout << "Give me your name and surname:"<<endl;//line 2
cin >> name;//line 3
int age;//line 4
cout << "Give me your age:" <<endl;//line 5
cin >> age;//line 6

ユーザーが最初の質問に「Arkadiusz Wlodarczyk」と答えるとどうなりますか?

プログラムを実行して、自分で確認してください。

コンソールに「Arkadiusz」と表示されますが、プログラムは「年齢」を尋ねません。 「Arkadiusz」を印刷するとすぐに終了します。

また、「Wlodarczyk」は表示されません。なくなったようです(?)*

どうした? ;-)

「Arkadiusz」と「Wlodarczyk」の間にスペースがあるためです。

名前と姓の間の「スペース」文字は、「入力」ストリームで抽出されるのを待機している2つの変数があることを示すコンピューターのサインです。

コンピューターは、複数の変数を入力するために送信しようとしていると考えています。その「スペース」サインは、彼がそのように解釈するためのサインです。

したがって、コンピューターは「名前」に「Arkadiusz」を割り当てます(2)。ストリーム(入力)に複数の文字列を配置するため、コンピューターは変数「年齢」に値「Wlodarczyk」を割り当てようとします(!)。その命令はすでに実行されているため(!)、ユーザーは6行目の「cin」に何も入力する機会がありません。どうして?ストリームにまだ何かが残っていたからです。前に述べたように、ストリームはフロー内にあるため、できるだけ早くすべてを削除する必要があります。そして、コンピューターが命令の年齢を見ると、その可能性が生まれました。

コンピューターは、誰かの年齢を格納する変数を作成したことを知りません(4行目)。 「年齢」は単なるラベルです。コンピューターの場合、「年齢」は「afsfasgfsagasggas」と呼ばれることもありますが、同じです。彼にとっては、コンピューターに(6)行目で指示/指示したため、「Wlodarczyk」を割り当てようとする変数にすぎません。

そうするのは間違っていますが、やったのはあなたです!それはあなたの責任です!まあ、多分ユーザーですが、それでも...


大丈夫大丈夫。しかし、それを修正する方法?!

いくつかの興味深いことを学ぶために適切に修正する前に、少し例を試してみましょう:-)

私は物事を理解するアプローチをすることを好みます。私たちがどのようにそれをしたかを知らずに何かを修正することは満足を与えません、そう思いませんか? :)

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate(); //new line is here :-)

上記のコードを呼び出すと、ストリームの状態(cin)が4(行7)に等しいことがわかります。これは、その内部状態がもうgoodbitと等しくないことを意味します。何かがめちゃくちゃです。それはかなり明白ですよね?文字列型の値( "Wlodarczyk")をint型変数 'age'に割り当てようとしました。タイプが一致しません。何かが間違っていることを知らせる時が来ました。コンピューターは、ストリームの内部状態を変更することでそれを行います。それは次のようなものです:「あなたは男をf ****修正してください。私はあなたに「親切に」知らせます;-)」

単に 'cin'(ストリーム)を使用できなくなります。立ち往生しています。水流に大きな木の丸太を置いたように。使用する前に修正する必要があります。木材のログ(内部状態)では許可されていないため、そのストリーム(cin)からデータ(水)を取得できなくなりました。

じゃあ、障害物(木の丸太)がある場合は、そうするために作られたツールを使ってそれを取り除くことができますか?

はい!

4に設定されたcinの内部状態は、ハウリングと音を立てているアラームのようなものです。

cin.clearは状態をクリアして通常の状態に戻します(goodbit)。あなたが来て警報を黙らせたかのようです。延期するだけです。あなたは何かが起こったのを知っているので、あなたは言う:「音を立てるのを止めてもいい。何かがすでに間違っていることを知っている、黙って(クリア)」.

よし、そうしよう! cin.clear()を使用してみましょう。

最初の入力として「Arkadiusz Wlodarczyk」を使用して以下のコードを呼び出します。

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl; 
cin.clear(); //new line is here :-)
cout << cin.rdstate()<< endl;  //new line is here :-)

上記のコードを実行すると、状態がgoodbitに等しいことが確実にわかります。

素晴らしいので問題は解決しましたか?

最初の入力として「Arkadiusz Wlodarczyk」を使用して以下のコードを呼び出します。

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl;; 
cin.clear(); 
cout << cin.rdstate() << endl; 
cin >> age;//new line is here :-)

行9の後に状態がgoodbitに設定されていても、ユーザーは「年齢」を求められません。プログラムは停止します。

なぜ?!

おっと...警報を発しました。水の中の木の丸太についてはどうでしょうか?.

その木片をストリームから「Wlodarczyk」削除する必要があります。アラームをオフにしても、問題はまったく解決しません。あなたはそれを黙らせただけで、問題はなくなったと思いますか? ;)

だから、別のツールの時間です:

cin.ignoreは、ストリームが詰まった木の丸太を取り除いて取り除くロープ付きの特別なトラックと比較できます。プログラムのユーザーが作成した問題をクリアします。

それで、アラームをオフにする前でも使用できますか?

はい:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;

「Wlodarczyk」は、7行目でノイズを出す前に削除されます。

10000と「\ n」とは何ですか?

'\ n'に達するまで(念のため)10000文字を削除する(ENTER)と表示されます。ところで、それはnumeric_limitsを使用してより良く行うことができますが、この答えのトピックではありません。


したがって、ノイズが発生する前に問題の主な原因はなくなりました...

なぜ「クリア」が必要なのですか?

たとえば、20行目ではなく「20歳」など、6行目で「年齢を教えて」という質問があった場合はどうなりますか。

タイプは再び一致しません。コンピューターは文字列をintに割り当てようとします。そして、アラームが開始されます。そのような状況に反応する機会さえありません。 cin.ignoreはそのような場合には役に立ちません。

そのため、そのような場合にはclearを使用する必要があります。

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;
cin.clear();
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

しかし、「万が一に備えて」州をクリアすべきですか?

もちろん違います。

問題が発生した場合(cin >> age;)、命令はfalseを返すことで通知します。

そのため、条件付きステートメントを使用して、ユーザーがストリームに間違った型を入力したかどうかを確認できます

int age;
if (cin >> age) //it's gonna return false if types doesn't match
    cout << "You put integer";
else
    cout << "You bad boy! it was supposed to be int";

それでは、たとえば次のような初期問題を修正できます。

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

int age;
cout << "Give me your age:" << endl;
if (cin >> age)
  cout << "Your age is equal to:" << endl;
else
{
 cin.clear();
 cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
 cout << "Give me your age name as string I dare you";
 cin >> age;
}

もちろん、これは、例えばloop whileを使用して問題の内容を実行することで改善できます。

ボーナス:

疑問に思うかもしれません。ユーザーから同じ行の名前と姓を取得したい場合はどうですか? cinが「スペース」で区切られた各値を異なる変数として解釈する場合、cinを使用することも可能ですか?

もちろん、次の2つの方法で実行できます。

1)

string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;

cout << "Hello, " << name << " " << surname << endl;

2)またはgetl​​ine関数を使用して。

getline(cin, nameOfStringVariable);

それがそれを行う方法です:

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

2番目のオプションは、getlineの前に「cin」を使用した後に使用した場合、裏目に出る可能性があります。

それをチェックしよう:

a)

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endl;

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

年齢として「20」を入力すると、nameAndSurnameの入力は求められません。

しかし、そのようにすると:

b)

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endll

すべて順調。

何?!

入力(ストリーム)に何かを置くたびに、最後の白い文字であるENTER( '\ n')のままにします。何らかの方法で値をコンソールに入力する必要があります。したがって、データがユーザーから来た場合に発生する必要があります。

b)cinの特徴は、空白を無視することです。そのため、cinから情報を読み込む場合、改行文字「\ n」は重要ではありません。無視されます。

a)getline関数は、改行文字( '\ n')までの行全体を取得し、改行文字が最初のものである場合、getline関数は '\ n'を取得します。 3行目のストリームに「20」を入力したユーザーがストリームに残した改行文字を抽出します。

したがって、それを修正するには、常にcin.ignore()を呼び出します。プログラム内でgetline()を使用する場合、cinを使用して値を取得するたびに。

したがって、適切なコードは次のようになります。

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cin.ignore(); // it ignores just enter without arguments being sent. it's same as cin.ignore(1, '\n') 
cout << "Your age is" << age << endl;


string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

ストリームがより明確になることを願っています。

沈黙してください! :-)

5
Morfidon

cin.ignore(1000,'\n')を使用して、バッファ内の前のcin.get()のすべての文字をクリアすると、 '\ n'または1000 charsに最初に会ったときに停止することを選択します。

2
Phy Lieng