入力を読み取った後、なぜcin.clear()とcin.ignore()を呼び出すのでしょうか。


86

Google CodeUniversityの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パラメータが必要なのはなぜですか?


1
これは、これまでで最も賛成の投稿です。私は高校でピークに達したに違いない。
JacKeown

回答:


102

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


57
+1。最大10000文字を無視するのではなく、を使用する方がよいことを追加したいと思いますcin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
デニス

30
を使用したい場合はstd::numeric_limits、必ず#include <limits>
gbmhunter 2014

1
誰かが構文を説明できますstd::numeric_limits<std::streamsize>::max()か?
ミントラン2017

4
@Minh Tran:std :: streamsizeは、転送されたI / O文字の数またはI / Oバッファのサイズを与える符号付き整数です。ここでは、「numeric_limits」テンプレートクラスを使用して、転送されるI / Oバッファまたは文字の最大制限を知りたいと考えました。
Pankaj 2017年

1
@Minh Tran:1つのマイナーな修正「streamsize」はクラスではなく、単なる整数型です。他の限界も取得できます。int、charなど。[リンク](en.cppreference.com/w/cpp/types/numeric_limits)で確認してください
Pankaj

46

あなたは入力します

if (!(cin >> input_var))

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

cin.clear();

エラーフラグを取り除くため。また、失敗した入力は、ある種のバッファであると私が想定しているものに置かれます。もう一度入力を取得しようとすると、バッファ内の同じ入力が読み取られ、再び失敗します。だからあなたは必要です

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

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


11
無視のデフォルトは1文字をスキップすることであるため、1行全体をスキップするにはより大きな数が必要です。
ボーパーソン2011

22

なぜ使用するのですか:

1)cin.ignore

2)cin.clear

単に:

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

2)ストリームの内部状態をクリアします。cin.clearを使用した後、内部状態は再びgoodbitに戻されます。これは、「エラー」がないことを意味します。

ロングバージョン:

'stream'(cin)に何かが置かれている場合は、そこから取得する必要があります。「取得」とは、「使用済み」、「削除済み」、「抽出済み」を意味します。ストリームには流れがあります。データは、ストリーム上の水のように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

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

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

コンソール「Arkadiusz」に表示されますが、プログラムは「年齢」を要求しません。「アルカディウス」を印刷した直後に終了します。

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

どうした?;-)

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

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

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

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

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

はい!

cinの内部状態を4に設定すると、ハウリングして音を立てるアラームのようなものになります。

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

大丈夫そうしましょう!cin.clear()を使用しましょう。

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

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に等しいことが確実にわかります。

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

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

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」について話したテキストに戻ってください。

その木片「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を使用するとより適切に実行できますが、この回答のトピックではありません。


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

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

たとえば、6行目で「年齢を教えて」という質問を誰かが求めた場合はどうなりますか。たとえば、20を書く代わりに「20歳」です。

タイプが再び一致しません。コンピュータは文字列を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;
}

もちろん、これは、たとえば、loopwhileを使用して問題の操作を実行することで改善できます。

ボーナス:

あなたは不思議に思うかもしれません。ユーザーから同じ行に名前と姓を取得したい場合はどうなりますか?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;

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

黙ってください!:-)


1

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.