为什么我们在读取输入后要调用 cin.clear() 和 cin.ignore() ?
Google 代码大学的 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
参数?
Google Code University's C++ tutorial used to have this code:
// 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;
}
What is the significance of cin.clear()
and cin.ignore()
? Why are the 10000
and \n
parameters necessary?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
cin.clear()
清除cin
上的错误标志(以便将来的 I/O 操作能够正常工作),然后cin.ignore(10000, '\n')
跳到下一个换行符(忽略与非数字同一行上的任何其他内容,这样就不会导致另一个解析失败)。它最多只会跳过 10000 个字符,因此代码假设用户不会输入很长的无效行。The
cin.clear()
clears the error flag oncin
(so that future I/O operations will work correctly), and thencin.ignore(10000, '\n')
skips to the next newline (to ignore anything else on the same line as the non-number so that it does not cause another parse failure). It will only skip up to 10000 characters, so the code is assuming the user will not put in a very long, invalid line.输入该
如果从 cin 获取输入时发生错误,则 语句。如果发生错误,则会设置错误标志,并且以后获取输入的尝试将失败。这就是为什么您需要
摆脱错误标志。另外,失败的输入将位于我认为某种缓冲区中。当您尝试再次获取输入时,它将读取缓冲区中的相同输入,并且会再次失败。这就是为什么你需要
从缓冲区中取出 10000 个字符,但如果遇到换行符 (\n) 则停止。 10000 只是一个通用的大值。
You enter the
statement if an error occurs when taking the input from cin. If an error occurs then an error flag is set and future attempts to get input will fail. That's why you need
to get rid of the error flag. Also, the input which failed will be sitting in what I assume is some sort of buffer. When you try to get input again, it will read the same input in the buffer and it will fail again. That's why you need
It takes out 10000 characters from the buffer but stops if it encounters a newline (\n). The 10000 is just a generic large value.
为什么我们使用:
1)cin.ignore
2)cin.clear
?
简单地说:
1)忽略(提取并丢弃)我们不希望在流中出现的值
2)清除流的内部状态。使用 cin.clear 后,内部状态再次设置回 goodbit,这意味着没有“错误”。
长版本:
如果将某些内容放入“流”(cin),则必须从那里取出它。 “采取”是指从流中“使用”、“删除”、“提取”。溪流有流动。数据在 cin 上流动,就像水在流动一样。你根本无法阻止水流;)
看这个例子:
如果用户对第一个问题回答:“Arkadiusz Wlodarczyk”,会发生什么?
运行该程序亲自看看。
您将在控制台上看到“Arkadiusz”,但程序不会询问您“年龄”。它会在打印“Arkadiusz”后立即完成。
并且“Wlodarczyk”没有显示。看起来好像消失了(?)*
发生了什么? ;-)
因为“Arkadiusz”和“Wlodarczyk”之间有一个空格。
名字和姓氏之间的“空格”字符是计算机的一个标志,表明“输入”流上有两个变量等待提取。
计算机认为您正在发送并输入多个变量。那个“空间”符号是他这样解释的符号。
因此,计算机将“Arkadiusz”分配给“name”(2),并且因为您在流(输入)上放置了多个字符串,计算机将尝试将值“Wlodarczyk”分配给变量“age”(!)。用户将没有机会在第 6 行的“cin”上放置任何内容,因为该指令已经执行(!)。为什么?因为还有一些东西在流中。正如我之前所说,流是流动的,因此必须尽快从中删除所有内容。当计算机看到指令 cin >> 时,可能性就出现了。年龄;
计算机不知道您创建了一个存储某人年龄的变量(第 4 行)。 “年龄”只是一个标签。对于计算机来说,“年龄”也可以称为:“afsfasgfsagasggas”,两者是相同的。对他来说,这只是一个变量,他会尝试将“Wlodarczyk”分配给它,因为您在第 (6) 行命令/指示计算机这样做。
这样做是错误的,但是嘿,这是你做的!都是你的错!好吧,也许是用户,但仍然......
好吧好吧。但如何解决呢?!
在我们正确修复它以学习一些更有趣的东西之前,让我们尝试先玩一下这个例子:-)
我更喜欢采用一种我们理解事物的方法。在不知道我们是如何做的情况下修复某些东西并不能带来满足感,你不觉得吗? :)
调用上面的代码后,您会注意到流的状态 (cin) 等于 4(第 7 行)。这意味着它的内部状态不再等于goodbit。有些事情搞砸了。这很明显,不是吗?您尝试将字符串类型值(“Wlodarczyk”)分配给 int 类型变量“age”。类型不匹配。是时候通知有问题了。计算机通过改变流的内部状态来实现这一点。就像:“你搞砸了,伙计,请修复我。我‘善意’地通知你;-)”
你根本不能再使用“cin”(流)了。它被卡住了。就像你把大木头放在水流上一样。您必须先修复它,然后才能使用它。无法再从该流(cin)中获取数据(水),因为木材日志(内部状态)不允许您这样做。
哦,那么如果有障碍物(木头),我们可以使用专门的工具将其移除吗?
是的!
cin 设置为 4 的内部状态就像一个嚎叫并发出噪音的警报。
cin.clear 将状态清除回正常状态 (goodbit)。就好像你来了并消除了警报一样。你只是把它推迟了。你知道发生了什么事,所以你说:“不要再吵闹了,我知道出了问题了,闭嘴(清楚)”。
好吧,我们就这样做吧!让我们使用 cin.clear()。
使用“Arkadiusz Wlodarczyk”作为第一个输入调用下面的代码:
执行上面的代码后,我们肯定可以看到状态等于 goodbit。
太好了,问题解决了吗?
使用“Arkadiusz Wlodarczyk”作为第一个输入调用以下代码:
即使在第 9 行之后状态设置为 goodbit,也不会询问用户“年龄”。程序停止。
为什么?!
哦天哪...你刚刚推迟了警报,那么水里的木头怎么样?*回到我们谈论“Wlodarczyk”的文本,它据说是如何消失的。
您需要从溪流中移除“Wlodarczyk”那块木头。关闭闹钟根本不能解决问题。你只是让它安静下来,你认为问题就消失了? ;)
所以是时候使用另一种工具了:
cin.ignore 可以比作一辆带有绳索的特殊卡车,它可以清除堵塞溪流的木头。它清除了程序用户创建的问题。
那么我们可以在闹钟响起之前使用它吗?
是的:
“Wlodarczyk”将在第 7 行发出噪音之前被删除
。10000 和 '\n' 是什么?
它表示删除 10000 个字符(以防万一)直到遇到 '\n' (ENTER)。顺便说一句,使用 numeric_limits 可以做得更好,但这不是这个答案的主题。
所以问题的主要原因在噪音产生之前就已经消失了……
那为什么我们需要“清除”呢?
如果有人在第 6 行询问“告诉我你的年龄”问题,例如:“二十岁”而不是写 20,该怎么办?
类型再次不匹配。计算机尝试将字符串分配给 int。警报开始。你甚至没有机会对这样的情况做出反应。在这种情况下 cin.ignore 不会帮助你。
因此,我们必须在这种情况下使用clear:
但是您应该“以防万一”清除状态吗?
当然不是。
如果出现问题(cin>>age;)指令将通过返回 false 来通知您。
因此,我们可以使用条件语句来检查用户是否在流上输入了错误的类型。
好吧,这样我们就可以解决我们最初的问题,例如:
当然,这可以通过使用循环 while 执行您所讨论的操作来改进。
奖励:
您可能想知道。如果我想从用户那里获取同一行中的姓名怎么办?如果 cin 将由“空格”分隔的每个值解释为不同的变量,是否可以使用 cin ?
当然,您可以通过两种方式完成:
1)
2)或使用 getline 函数。
这就是如何做到这一点:
如果您在 getline 之前使用“cin”之后使用第二个选项,则可能会适得其反。
让我们检查一下:
a)
如果您输入“20”作为年龄,则不会询问您的姓名。
但如果你这样做:
b)
一切都很好。
什么?!
每次你在输入(流)上放置一些东西时,你都会在末尾留下白色字符,即 ENTER('\n'),你必须以某种方式输入值到控制台。因此,如果数据来自用户,则必然会发生。
b) cin 的特点是它忽略空格,所以当你从 cin 中读取信息时,换行符 '\n' 并不重要。它会被忽略。
a) getline 函数获取到换行符 ('\n') 之前的整行,当换行符是第一个字符时,getline 函数获取 '\n',这就是要获取的全部内容。您提取在第 3 行中将“20”放入流中的用户留在流中的换行符。
因此,为了修复它,请始终调用 cin.ignore();如果您要在程序中使用 getline(),则每次使用 cin 获取任何值时。
所以正确的代码是:
我希望流对你来说更清楚。
哈请让我安静! :-)
Why do we use:
1) cin.ignore
2) cin.clear
?
Simply:
1) To ignore (extract and discard) values that we don't want on the stream
2) To clear the internal state of stream. After using cin.clear internal state is set again back to goodbit, which means that there are no 'errors'.
Long version:
If something is put on 'stream' (cin) then it must be taken from there. By 'taken' we mean 'used', 'removed', 'extracted' from stream. Stream has a flow. The data is flowing on cin like water on stream. You simply cannot stop the flow of water ;)
Look at the example:
What happens if the user answers: "Arkadiusz Wlodarczyk" for first question?
Run the program to see for yourself.
You will see on console "Arkadiusz" but program won't ask you for 'age'. It will just finish immediately right after printing "Arkadiusz".
And "Wlodarczyk" is not shown. It seems like if it was gone (?)*
What happened? ;-)
Because there is a space between "Arkadiusz" and "Wlodarczyk".
"space" character between the name and surname is a sign for computer that there are two variables waiting to be extracted on 'input' stream.
The computer thinks that you are tying to send to input more than one variable. That "space" sign is a sign for him to interpret it that way.
So computer assigns "Arkadiusz" to 'name' (2) and because you put more than one string on stream (input) computer will try to assign value "Wlodarczyk" to variable 'age' (!). The user won't have a chance to put anything on the 'cin' in line 6 because that instruction was already executed(!). Why? Because there was still something left on stream. And as I said earlier stream is in a flow so everything must be removed from it as soon as possible. And the possibility came when computer saw instruction cin >> age;
Computer doesn't know that you created a variable that stores age of somebody (line 4). 'age' is merely a label. For computer 'age' could be as well called: 'afsfasgfsagasggas' and it would be the same. For him it's just a variable that he will try to assign "Wlodarczyk" to because you ordered/instructed computer to do so in line (6).
It's wrong to do so, but hey it's you who did it! It's your fault! Well, maybe user, but still...
All right all right. But how to fix it?!
Let's try to play with that example a bit before we fix it properly to learn a few more interesting things :-)
I prefer to make an approach where we understand things. Fixing something without knowledge how we did it doesn't give satisfaction, don't you think? :)
After invoking above code you will notice that the state of your stream (cin) is equal to 4 (line 7). Which means its internal state is no longer equal to goodbit. Something is messed up. It's pretty obvious, isn't it? You tried to assign string type value ("Wlodarczyk") to int type variable 'age'. Types doesn't match. It's time to inform that something is wrong. And computer does it by changing internal state of stream. It's like: "You f**** up man, fix me please. I inform you 'kindly' ;-)"
You simply cannot use 'cin' (stream) anymore. It's stuck. Like if you had put big wood logs on water stream. You must fix it before you can use it. Data (water) cannot be obtained from that stream(cin) anymore because log of wood (internal state) doesn't allow you to do so.
Oh so if there is an obstacle (wood logs) we can just remove it using tools that is made to do so?
Yes!
internal state of cin set to 4 is like an alarm that is howling and making noise.
cin.clear clears the state back to normal (goodbit). It's like if you had come and silenced the alarm. You just put it off. You know something happened so you say: "It's OK to stop making noise, I know something is wrong already, shut up (clear)".
All right let's do so! Let's use cin.clear().
Invoke below code using "Arkadiusz Wlodarczyk" as first input:
We can surely see after executing above code that the state is equal to goodbit.
Great so the problem is solved?
Invoke below code using "Arkadiusz Wlodarczyk" as first input:
Even tho the state is set to goodbit after line 9 the user is not asked for "age". The program stops.
WHY?!
Oh man... You've just put off alarm, what about the wood log inside a water?* Go back to text where we talked about "Wlodarczyk" how it supposedly was gone.
You need to remove "Wlodarczyk" that piece of wood from stream. Turning off alarms doesn't solve the problem at all. You've just silenced it and you think the problem is gone? ;)
So it's time for another tool:
cin.ignore can be compared to a special truck with ropes that comes and removes the wood logs that got the stream stuck. It clears the problem the user of your program created.
So could we use it even before making the alarm goes off?
Yes:
The "Wlodarczyk" is gonna be removed before making the noise in line 7.
What is 10000 and '\n'?
It says remove 10000 characters (just in case) until '\n' is met (ENTER). BTW It can be done better using numeric_limits but it's not the topic of this answer.
So the main cause of problem is gone before noise was made...
Why do we need 'clear' then?
What if someone had asked for 'give me your age' question in line 6 for example: "twenty years old" instead of writing 20?
Types doesn't match again. Computer tries to assign string to int. And alarm starts. You don't have a chance to even react on situation like that. cin.ignore won't help you in case like that.
So we must use clear in case like that:
But should you clear the state 'just in case'?
Of course not.
If something goes wrong (cin >> age;) instruction is gonna inform you about it by returning false.
So we can use conditional statement to check if the user put wrong type on the stream
All right so we can fix our initial problem like for example that:
Of course this can be improved by for example doing what you did in question using loop while.
BONUS:
You might be wondering. What about if I wanted to get name and surname in the same line from the user? Is it even possible using cin if cin interprets each value separated by "space" as different variable?
Sure, you can do it two ways:
1)
2) or by using getline function.
and that's how to do it:
The second option might backfire you in case you use it after you use 'cin' before the getline.
Let's check it out:
a)
If you put "20" as age you won't be asked for nameAndSurname.
But if you do it that way:
b)
everything is fine.
WHAT?!
Every time you put something on input (stream) you leave at the end white character which is ENTER ('\n') You have to somehow enter values to console. So it must happen if the data comes from user.
b) cin characteristics is that it ignores whitespace, so when you are reading in information from cin, the newline character '\n' doesn't matter. It gets ignored.
a) getline function gets the entire line up to the newline character ('\n'), and when the newline char is the first thing the getline function gets '\n', and that's all to get. You extract newline character that was left on stream by user who put "20" on stream in line 3.
So in order to fix it is to always invoke cin.ignore(); each time you use cin to get any value if you are ever going to use getline() inside your program.
So the proper code would be:
I hope streams are more clear to you know.
Hah silence me please! :-)
使用
cin.ignore(1000,'\n')
清除缓冲区中之前cin.get()
的所有字符,当它到达时会选择停止首先满足 '\n' 或1000 个字符
。use
cin.ignore(1000,'\n')
to clear all of chars of the previouscin.get()
in the buffer and it will choose to stop when it meet '\n' or1000 chars
first.