为什么我们要使用:
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'上放置任何内容,因为该指令已经执行(!)。为什么?因为流中仍然有一些内容。正如我之前所说,流是连续不断的,因此必须尽快从中删除所有内容。当计算机看到指令cin >> age;时,这种可能性就出现了。
计算机不知道您创建了一个存储某人年龄的变量(第4行)。对于计算机来说,“age”只是一个标签。对于计算机来说,“age”也可以称为:“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();
调用上述代码后,您会注意到流(cin)的状态等于4(第7行)。这意味着它的内部状态不再等于goodbit。出了点问题。很明显,你试图将字符串类型值(“Wlodarczyk”)分配给int类型变量'age'。类型不匹配。现在是通知有问题的时候了。计算机通过更改流的内部状态来表示这一点。就像:“你搞砸了,修复我吧。我“友好地”通知你;-)”
您不能再使用'cin'(流)。它被卡住了。就像你把大木头放在水流上一样。您必须在使用它之前修复它。因为木头的障碍(内部状态),数据(水)无法从该流(cin)中获取。
哦,所以如果有障碍物(木材),我们可以使用专门用于此目的的工具将其移除?
是的!
cin的内部状态设置为4就像正在发出警报并发出噪音。
cin.clear可以将状态清除回到正常状态(goodbit)。就像你来了并且关闭了警报一样。你只是把它关掉了。你知道发生了什么事情,所以你说:“停止发出噪音,我已经知道出了什么问题,闭嘴(clear)”。
好的,让我们这样做!让我们使用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();
cout << cin.rdstate()<< endl;
我们可以在执行上述代码后看到状态等于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;
尽管在第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,会怎样呢?
类型又不匹配了。计算机试图将字符串分配给整数。警报开始响起。你甚至没有机会对这种情况做出反应。在这种情况下,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');
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');
cout << "Give me your age name as string I dare you";
cin >> age;
}
当然,这可以通过使用while循环来改进,例如在问题中所做的。
奖励:
你可能会想知道。如果我想从用户那里获取姓名和姓氏并将它们放在同一行中,该怎么办?如果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”,则不会要求输入姓名和姓氏。
但是,如果您这样做:
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函数获取的第一件事时,getline函数获取'\n',这就是全部内容。您提取了由在第3行将“20”放入流中的用户留下的换行符。
因此,为了修复它,每次使用cin获取任何值时,如果您将在程序中使用getline(),则始终调用cin.ignore()。
因此,正确的代码应该是:
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;
希望现在流更清晰了。
哈哈请让我安静!:-)