提问人:JacKeown 提问时间:2/27/2011 最后编辑:Ardent CoderJacKeown 更新时间:4/3/2020 访问量:287521
为什么我们会在读取输入后调用 cin.clear() 和 cin.ignore()?
Why would we call cin.clear() and cin.ignore() after reading input?
问:
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;
}
和 的意义是什么?为什么需要 and 参数?cin.clear()
cin.ignore()
10000
\n
答:
清除错误标志(以便将来的 I/O 操作正常工作),然后跳到下一个换行符(忽略与非数字在同一行上的任何其他内容,以免导致另一次分析失败)。它最多只能跳过 10000 个字符,因此代码假设用户不会输入很长的无效行。cin.clear()
cin
cin.ignore(10000, '\n')
评论
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::numeric_limits
#include <limits>
std::numeric_limits<std::streamsize>::max()
您输入
if (!(cin >> input_var))
语句,如果从 CIN 获取输入时发生错误。如果发生错误,则设置错误标志,将来尝试获取输入将失败。这就是为什么你需要
cin.clear();
以消除错误标志。此外,失败的输入将位于我认为是某种缓冲区中。当您尝试再次获取输入时,它将读取缓冲区中的相同输入,并且将再次失败。这就是为什么你需要
cin.ignore(10000,'\n');
它从缓冲区中取出 10000 个字符,但如果遇到换行符 (\n),则停止。10000 只是一个通用的大值。
评论
用于清除缓冲区中前一个字符的所有字符,它将在遇到“\n”或第一个字符时选择停止。cin.ignore(1000,'\n')
cin.get()
1000 chars
我们为什么使用:
1) cin.ignore
2) cin.clear
?
只是:
1) 忽略(提取和丢弃)我们不需要的值
2)清除流的内部状态。使用 cin.clear 后,内部状态再次设置回 goodbit,这意味着没有“错误”。
长版:
如果某物被放在“流”(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”分配给“name”(2),并且由于您在流(输入)上放置了多个字符串,因此计算机将尝试将值“Wlodarczyk”分配给变量“age”(!)。用户将没有机会在第 6 行的“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;
}
当然,这可以通过例如使用 loop while 执行您所做的相关操作来改进。
奖金:
你可能想知道。如果我想从用户那里获得同一行中的名字和姓氏怎么办?如果 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();每次使用 CIN 获取任何值时,如果您打算在程序中使用 getline()。
因此,正确的代码是:
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;
我希望你知道的溪流更清楚。
呵呵,请让我安静!:-)
评论