为什么我们在读取输入后调用cin.clear()和cin.ignore()?

时间:2011-02-27 05:17:35

标签: c++ input iostream

Google Code University's C++ tutorial过去有这段代码:

// 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参数?

4 个答案:

答案 0 :(得分:84)

cin.clear()清除cin上的错误标记(以便将来的I / O操作正常工作),然后cin.ignore(10000, '\n')跳到下一个换行符(忽略其他任何内容)与非数字相同的行,以便它不会导致另一个解析失败)。它最多只会跳过10000个字符,所以代码假设用户不会放入很长的无效行。

答案 1 :(得分:31)

您输入

if (!(cin >> input_var))

声明从cin获取输入时是否发生错误。如果发生错误,则设置错误标志,并且将来尝试获取输入将失败。这就是你需要的原因

cin.clear();

摆脱错误标志。此外,失败的输入将坐在我认为是某种缓冲区的位置。当您尝试再次输入时,它将读取缓冲区中的相同输入,它将再次失败。这就是你需要的原因

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

它从缓冲区中取出10000个字符,但如果遇到换行符(\ n)则停止。 10000只是一个通用的大值。

答案 2 :(得分:4)

我们为什么使用:

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

如果用户回答:第一个问题是“ Arkadiusz Wlodarczyk”,会发生什么?

运行该程序供自己查看。

您将在控制台“ Arkadiusz”上看到,但程序不会要求您输入“年龄”。它将在打印“ Arkadiusz”之后立即完成。

“ Wlodarczyk”未显示。好像不见了(?)*

发生了什么事? ;-)

因为“ Arkadiusz”和“ Wlodarczyk”之间有空格。

名称和姓氏之间的“空格”字符表示计算机上有两个变量等待在“输入”流上提取。

计算机认为您要发送多个输入变量。这个“空格”标志是他用这种方式解释的标志。

因此,计算机将“ Arkadiusz”分配给“名称”(2),并且由于您在流(输入)上放置了多个字符串,因此计算机将尝试将值“ Wlodarczyk”分配给变量“ age”(!)。用户将没有机会在第6行的'cin'上放置任何内容,因为该指令已被执行(!)。为什么?因为仍然有东西在流。正如我之前所说的,流正在流动中,因此必须尽快从中删除所有内容。当计算机看到指令cin >>年龄时,这种可能性就来了;

计算机不知道您创建了一个存储某人年龄的变量(第4行)。 “年龄”仅仅是一个标签。对于计算机,“年龄”可以这样称呼:“ afsfasgfsagasggas”,并且它是相同的。对他来说,这只是一个变量,他将尝试为其分配“ Wlodarczyk”,因为您在第(6)行中命令/指示计算机这样做。

这样做是错误的,但是嘿,是你做的!这是你的错!好吧,也许是用户,但仍然...


好的,好的。但是如何解决?!

让我们尝试一下该示例,然后再适当修复它,以学习更多有趣的东西:-)

我更喜欢采取一种我们了解事物的方法。不知道我们如何做而修复的东西并不能使您满意,您不觉得吗? :)

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'。类型不匹配。现在该通知某些问题了。而计算机则通过更改流的内部状态来实现。就像是:“你这家伙,请修理我。我会'善意地'告诉你;-)“

您根本无法再使用“ cin”(流)。卡住了就像您将大木头原木放在水流上一样。必须先修复它,然后才能使用它。无法再从该流(cin)中获取数据(水),因为原木(内部状态)不允许您这样做。

哦,如果有障碍物(原木),我们可以使用专用的工具将其清除吗?

是的!

cin的内部状态设置为4就像是发出啸叫声并发出声音的警报。

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”的文字应该消失的地方。

您需要从流中移除那块木头“ 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;

在第7行发出声音之前,将删除“ Wlodarczyk”。

什么是10000和'\ n'?

它表示删除10000个字符(以防万一),直到遇到'\ n'(ENTER)。顺便说一句,使用numeric_limits可以做得更好,但这不是此答案的主题。


所以造成问题的主要原因在发出噪音之前就消失了...

那我们为什么需要“清除”?

如果有人在第6行中问“给我你的年龄”问题,例如:“二十岁”而不是写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;
}

当然可以通过例如使用循环而做有问题的事情来改善这一点。

奖金:

您可能想知道。如果我想从用户的同一行中获取姓名和姓怎么办?如果cin将用“空格”分隔的每个值解释为不同的变量,甚至可以使用cin?

当然,您可以通过两种方式做到这一点:

1)

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

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

2)或使用getline函数。

getline(cin, nameOfStringVariable);

这就是方法:

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

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

第二个选项可能会适得其反,以防您在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;

希望您能更清楚地了解信息流。

请让我安静! :-)

答案 3 :(得分:1)

使用cin.ignore(1000,'\n')清除缓冲区中之前cin.get()的所有字符,它会选择在符合&#39; \ n&#39;或1000 chars首先。