C++ 为什么我们会在读取输入后调用 cin.clear() 和 cin.ignore() ?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5131647/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why would we call cin.clear() and cin.ignore() after reading input?
提问by Hymaneown
Google Code University's C++ tutorialused to have this code:
谷歌代码大学的 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;
}
What is the significance of cin.clear()
and cin.ignore()
? Why are the 10000
and \n
parameters necessary?
cin.clear()
和的意义是cin.ignore()
什么?为什么需要10000
和\n
参数?
回答by Jeremiah Willcock
The cin.clear()
clears the error flag on cin
(so that future I/O operations will work correctly), and then cin.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.clear()
上清除错误标志cin
(以便将来的I / O操作将正常工作),然后cin.ignore(10000, '\n')
跳到下一个换行符(忽略任何其他在同一行作为非数量,使得它不会引起另一解析失败) . 它最多只能跳过 10000 个字符,因此代码假设用户不会输入很长的无效行。
回答by flight
You enter the
你输入
if (!(cin >> input_var))
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
如果从 cin 获取输入时发生错误,则为语句。如果发生错误,则会设置错误标志,并且将来获取输入的尝试将失败。这就是为什么你需要
cin.clear();
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
摆脱错误标志。此外,失败的输入将位于我假设的某种缓冲区中。当您再次尝试获取输入时,它将读取缓冲区中的相同输入,并且再次失败。这就是为什么你需要
cin.ignore(10000,'\n');
It takes out 10000 characters from the buffer but stops if it encounters a newline (\n). The 10000 is just a generic large value.
它从缓冲区中取出 10000 个字符,但在遇到换行符 (\n) 时停止。10000 只是一个通用的大值。
回答by Morfidon
Why do we use:
我们为什么使用:
1) cin.ignore
1) cin.ignore
2) cin.clear
2) cin.clear
?
?
Simply:
简单地:
1) To ignore (extract and discard) values that we don't want on the stream
1)忽略(提取和丢弃)我们不想要的值
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'.
2) 清除流的内部状态。使用 cin.clear 后,内部状态再次设置回 goodbit,这意味着没有“错误”。
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 ;)
如果某些东西放在“流”(cin)上,那么它必须从那里取出。“采取”是指从流中“使用”、“移除”、“提取”。流有流。数据像流水一样在cin上流动。你根本无法阻止水流;)
Look at the example:
看例子:
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
What happens if the user answers: "Arkadiusz Wlodarczyk" for first question?
如果用户对第一个问题回答“Arkadiusz Wlodarczyk”会发生什么?
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".
您将在控制台“Arkadiusz”上看到,但程序不会询问您“年龄”。它会在打印“Arkadiusz”后立即完成。
And "Wlodarczyk" is not shown. It seems like if it was gone (?)*
并且没有显示“Wlodarczyk”。好像它消失了(?)*
What happened? ;-)
发生了什么?;-)
Because there is a space between "Arkadiusz" and "Wlodarczyk".
因为“Arkadiusz”和“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;
因此计算机将“Arkadiusz”分配给“name”(2),并且由于您在流(输入)上放置了多个字符串,因此计算机将尝试将值“Wlodarczyk”分配给变量“age”(!)。用户将没有机会在第 6 行的 'cin' 上添加任何内容,因为该指令已经执行(!)。为什么?因为还有一些东西在流。正如我之前所说,流处于流动中,因此必须尽快将所有内容从中删除。当计算机看到指令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).
计算机不知道您创建了一个存储某人年龄的变量(第 4 行)。“年龄”只是一个标签。对于计算机,“时代”也可以称为:“afsfasgfsagasggas”,并且是相同的。对他来说,这只是一个变量,他会尝试将“Wlodarczyk”分配给它,因为您在第 (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? :)
我更喜欢采用一种我们理解事物的方法。在不知道我们是怎么做的情况下修理东西不会让人满意,你不觉得吗?:)
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 :-)
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' ;-)"
调用上述代码后,您会注意到流 (cin) 的状态等于 4(第 7 行)。这意味着它的内部状态不再等于 goodbit。有什么事情搞砸了。这很明显,不是吗?您尝试将字符串类型值 ("Wlodarczyk") 分配给 int 类型变量 'age'。类型不匹配。是时候通知出现问题了。计算机通过改变流的内部状态来做到这一点。就像:“你他妈的男人,请修理我。我'亲切地'通知你;-)”
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.
您根本无法再使用“cin”(流)。它卡住了。就像你把大木头放在水流上一样。您必须先修复它,然后才能使用它。无法再从该流(cin)中获取数据(水),因为木材日志(内部状态)不允许您这样做。
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 设置为 4 的内部状态就像一个警报,它在嚎叫和发出噪音。
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)".
cin.clear 清除状态恢复正常(goodbit)。就好像你来了然后把闹钟静音了一样。你只是把它关掉。你知道发生了一些事情,所以你说:“停止制造噪音是可以的,我知道有些事情不对劲,闭嘴(清楚)”。
All right let's do so! Let's use cin.clear().
好吧,让我们这样做!让我们使用 cin.clear()。
Invoke below code using "Arkadiusz Wlodarczyk" as first input:
使用“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 :-)
We can surely see after executing above code that the state is equal to goodbit.
执行上面的代码后我们肯定可以看到状态等于goodbit。
Great so the problem is solved?
太好了,问题解决了吗?
Invoke below code using "Arkadiusz Wlodarczyk" as first input:
使用“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 :-)
Even tho the state is set to goodbit after line 9 the user is not asked for "age". The program stops.
即使在第 9 行之后将状态设置为 goodbit,也不会要求用户提供“年龄”。程序停止。
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.
哦,伙计......你刚刚解除警报,水中的木头怎么样?* 回到我们谈论“Wlodarczyk”它应该如何消失的文本。
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? ;)
您需要从溪流中移除“Wlodarczyk”那块木头。关闭闹钟根本解决不了问题。你刚刚把它静音了,你认为问题就解决了?;)
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.
cin.ignore 可以比作一辆带有绳索的特殊卡车,它可以运走卡住溪流的原木。它清除了程序用户创建的问题。
So could we use it even before making the alarm goes off?
那么我们可以在闹钟响之前使用它吗?
Yes:
是的:
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;
The "Wlodarczyk" is gonna be removed before making the noise in line 7.
“Wlodarczyk”将在第 7 行发出噪音之前被删除。
What is 10000 and '\n'?
什么是 10000 和 '\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.
它说删除 10000 个字符(以防万一),直到遇到 '\n'(回车)。顺便说一句,使用 numeric_limits 可以做得更好,但这不是这个答案的主题。
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?
如果有人在第 6 行问“给我你的年龄”问题,例如:“二十岁”而不是写 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.
类型再次不匹配。计算机尝试将字符串分配给 int。并且警报开始。你甚至没有机会对这样的情况做出反应。cin.ignore 在这种情况下不会帮助你。
So we must use clear in case like that:
所以我们必须在这种情况下使用 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
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.
如果出现问题 (cin >> age;) 指令将通过返回 false 来通知您。
So we can use conditional statement to check if the user put wrong type on the stream
所以我们可以使用条件语句来检查用户是否在流上输入了错误的类型
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";
All right so we can fix our initial problem like for example that:
好的,所以我们可以解决我们最初的问题,例如:
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;
}
Of course this can be improved by for example doing what you did in question using loop while.
当然,这可以通过例如使用 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?
你可能想知道。如果我想在同一行中从用户那里获取姓名怎么办?如果 cin 将每个由“空格”分隔的值解释为不同的变量,甚至可以使用 cin 吗?
Sure, you can do it two ways:
当然,您可以通过两种方式做到这一点:
1)
1)
string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;
cout << "Hello, " << name << " " << surname << endl;
2) or by using getline function.
2) 或使用 getline 函数。
getline(cin, nameOfStringVariable);
and that's how to do it:
这就是如何做到这一点:
string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);
cout << "Hello, " << nameAndSurname << endl;
The second option might backfire you in case you use it after you use 'cin' before the getline.
如果您在 getline 之前使用 'cin' 之后使用它,则第二个选项可能会适得其反。
Let's check it out:
让我们来看看:
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;
If you put "20" as age you won't be asked for nameAndSurname.
如果您将“20”作为年龄,则不会要求您提供 nameAndSurname。
But if you do it that way:
但如果你这样做:
b)
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
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.
每次在输入(流)上输入内容时,都会在末尾留下白色字符,即 ENTER ('\n') 您必须以某种方式输入值到控制台。因此,如果数据来自用户,则必须发生这种情况。
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.
b) cin 的特点是它忽略空格,所以当你从 cin 读入信息时,换行符 '\n' 无关紧要。它被忽略。
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.
a) getline 函数获取整行直到换行符 ('\n'),当换行符是第一件事时,getline 函数获取 '\n',这就是全部。您提取在第 3 行中将“20”放入流中的用户留在流中的换行符。
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.
所以为了修复它总是调用cin.ignore(); 如果您打算在程序中使用 getline() ,则每次使用 cin 获取任何值时。
So the proper code would be:
所以正确的代码是:
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;
I hope streams are more clear to you know.
我希望你知道流更清楚。
Hah silence me please! :-)
哈请让我安静!:-)
回答by Phy Lieng
use cin.ignore(1000,'\n')
to clear all of chars of the previous cin.get()
in the buffer and it will choose to stop when it meet '\n' or 1000 chars
first.
用于cin.ignore(1000,'\n')
清除cin.get()
缓冲区中前一个字符的所有字符,它会选择在遇到 '\n' 或1000 chars
第一个时停止。