104

谷歌代码大学的 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参数?

4

4 回答 4

120

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

于 2011-02-27T05:36:48.837 回答
54

你输入

if (!(cin >> input_var))

如果从 cin 获取输入时发生错误,则声明。如果发生错误,则设置错误标志,以后尝试获取输入将失败。这就是为什么你需要

cin.clear();

摆脱错误标志。此外,失败的输入将位于我假设的某种缓冲区中。当您再次尝试获取输入时,它将在缓冲区中读取相同的输入,并且会再次失败。这就是为什么你需要

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

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

于 2011-02-27T05:44:44.490 回答
35

我们为什么使用:

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

计算机不知道您创建了一个存储某人年龄的变量(第 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 :-)

执行上述代码后,我们肯定可以看到 state 等于 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' 之后使用它。

让我们来看看:

一种)

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 函数获取 '\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;

我希望你知道流更清楚。

哈请让我安静!:-)

于 2018-10-26T17:21:23.203 回答
1

用于cin.ignore(1000,'\n')清除cin.get()缓冲区中前一个字符的所有字符,它会选择在遇到 '\n' 或1000 chars第一个时停止。

于 2015-12-20T10:19:08.713 回答