【问题标题】:C++ : if condition is evaluated with bad input (float) despite supposedly checking for it with cin.clear() and cin.ignore()C++:如果条件是用错误的输入(浮点数)评估的,尽管应该用 cin.clear() 和 cin.ignore() 检查它
【发布时间】:2018-04-01 14:47:58
【问题描述】:

认为我理解使用 cin.clear() 和 cin.ignore() 处理错误输入,就像在 here 中解释的那样,但在以下示例中

#include <iostream>
#include <limits>
using namespace std; //I know that this isn't good practice.

int main () {
    int a, b;

    while (cout << "Input some int: " && !(cin >> a)) {
        cout << "Wrong datatype!\n";
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }

    while (cout << "Input some int: " && !(cin >> b)) {
        cout << "Wrong datatype!\n";
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }

    if (a > 1) cout << "Some event.\n";
    if (b > 1) cout << "Some other event.\n";

    return 0;
}

我想要的行为只有在不需要的输入是某个字符时才会出现。 因此,如果我输入 x 和 y,我将再次被要求输入两个 int 并获得适当的输出,如果我输入一个 char 和一个 int 两次也是如此。

但是:如果我输入 2.3,我会得到

输入一些整数:错误的数据类型!

但没有机会更正我的输入,因为结果总是输出“某些事件”。第二个提示只是立即接受浮点数。

【问题讨论】:

  • using namespace std; //I know that this isn't good practice. 这是健壮、紧凑、最小程序的好习惯。
  • 为什么你输入浮点数但你的数据类型是整数?
  • 测试他/她的程序@Matthew.
  • 请注意 2 是一个有效的 int,即使 2.3 不是。流与有效 int 之后的内容无关。

标签: c++ if-statement io cin


【解决方案1】:

实际上,2.3 中的 2 被第一个提示接受,而 .3 留在输入缓冲区中。您看到的Wrong datatype! 来自第二个提示,看到.,它不是整数的有效字符。然后,我假设您输入一个整数,该整数被您的第二个提示接受。

【讨论】:

    【解决方案2】:

    这种基本方法很脆弱,而且容易出错。

    您的明显意图是接受一行输入并对其进行处理。如果是这样,那么执行此操作的正确函数是std::getline()。这就是它的目的。这正是它的作用。 &gt;&gt; 运算符不这样做。那不是它的用途。当然,通过使用各种辅助方法,如ignore()clear(),仍然可以实现这一目标,但是,正如您所发现的,正确使用这些功能并不直观。当然,您可以花大量时间翻阅他们的文档以了解他们的每一个语义行为,但是当您可以简单地使用 std::getline() 时,何必费心去做其他事情。这样做更容易。

    当然,一旦收到一行输入,您希望将其解析为整数。 现在是使用&gt;&gt;解析它的正确时间:

    std::string line;
    
    if (std::getline(line, std::cin))
    {
        std::istringstream i{line};
    
        int n;
    
        if (i >> n)
        {
               // Input parsed
        }
    }
    

    这不是更简单、更直接、更简单吗?当然,在这里输入“2.3”会导致&gt;&gt;运算符解析“2”,并成功,留下“.3”未解析。如果您想检测这种情况,只需使用get() 查看std::istringstream 中还剩下什么。如果您愿意,可以接受任何尾随空格。

    【讨论】:

    • 这不是 C++ I/O 流应该被使用的方式。您可以直接从 cin 读取一个整数并通过 cin.get() 检查下一个读取的字符是否为 '\n'。如果不是,则在用户按 Enter 之前,流中存在不可接受的字符。将字符串和字符串流与 getline 一起使用非常低效,这是许多人说 C++ I/O 速度慢的原因——事实并非如此。但是没有正确使用。
    • 除了检查 cin.get() 是不够的。您还必须检查格式化输入操作是否失败。然后,您必须清除错误条件。然后,您可能需要也可能不需要丢弃未处理的输入。突然之间,事情并不像起初看起来那么简单,您很快就会迷失在杂草中。手头的任务不是“从 cin 中读取一个整数”,而是“从 cin 中读取一整行,并验证它是否包含一个整数”。很大的区别。如果性能是一个问题,那么您一开始就不会使用流。
    • @山姆:当然。我在我的回答中写了一个例子来完成所有这些。不要误会我的意思,我也不是 C++ I/O 流的忠实粉丝,它是 80 年代的设计,具有奇怪的机制。但士气应该是:要么正确使用它,要么根本不使用它。
    • 您的示例 @Jodocus 似乎接受带有尾随空格的输入,如果每个输入都单独提示,则不会被丢弃,保持未读状态,并且在下一次提示用户后搞砸输入,如原来的问题是。谢谢你证明我的观点。
    • stackoverflow.com 上一定有一些 bug 让你看不到我的 cmets,它遵循代码。
    【解决方案3】:

    这里的问题是,当您在int 中输入类似2.3 的内容时,cin 是可以接受的。它读取2,看到.,因此它停止读取并将2 存储在变量中,并将.3 留在缓冲区中以供下次调用。所以,你通过第一个循环,进入第二个循环,然后你失败了,因为它试图将.读入b。然后你清除.3,你可以输入另一个输入。如果你输入另一个2.3,同样的事情会发生,b 会得到2,程序继续。

    读入输入的“防弹”方式是将其作为std::string 读入,然后对其进行解析以确保完整的输入是好的。看起来像

    std::string line;
    while (cout << "Input some int: " && std::getline(cin, line)) {
        std::stringstream ss(line);
        ss >> a;
        if (ss.eof()) // we did consume all the input
            break;
        else
            cout << "Wrong datatype!\n";
    }
    
    while (cout << "Input some int: " && std::getline(cin, line)) {
        std::stringstream ss(line);
        ss >> b;
        if (ss.eof()) // we did consume all the input
            break;
        else
            cout << "Wrong datatype!\n";
    }
    

    【讨论】:

    • 不,这个解决方案一点都不好。通过 cin 进行未格式化的输入是非常低效的,而只是通过 stringstreams 执行另一个格式化的输入。您应该阅读 int 然后通过 cin.get() 检查下一个字符是否实际上是换行符。如果不是,则流中有不可接受的内容。
    • @Jodocus 您必须测试每个空格字符,而不仅仅是换行符。我没有编写所有代码,而是选择了一条我知道不能破坏的更昂贵的路径。如果您正在等待用户输入,您可以通过这种方式节省“几个”周期。
    • 添加条件没问题 || std::isspace() 也在那里。
    【解决方案4】:

    当您输入“2.3”时,cin 将停在“.”处,并将“2”解释为所需的输入。

    然后,您将清除cin,当'.'遇到,丢弃3。

    如果你然后输入一个新的整数,它会接受它。

    【讨论】:

      【解决方案5】:

      这里的许多答案建议使用 std::getline 和字符串解析,或者使用字符串函数或字符串流。这是非常低效的,也不是应该使用流的方式。

      而是在数据仍在输入流中时对其进行解析:

      #include <iostream>
      #include <cctype>
      #include <limits>
      
      struct read_int {
          int& a;
      
          read_int(int& aa) : a{ aa } { }
          friend std::istream& operator >>(std::istream& is, read_int& ri) {
              char delim;
              while(!(is >> ri.a) || (delim = is.get(), delim != '\n' && !std::isspace(delim))) {
                  std::cerr << "Bad!\n";
                  is.clear();
                  is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
              }
              return is;
          }
      };
      
      
      int main() {
          int a, b;
          std::cin >> read_int(a) >> read_int(b);
          std::cout << a << ' ' << b;
          return 0;
      }
      

      此函数将接受“4 5”或“4\n6”之类的输入,但会为“4.2”之类的数据请求新输入,丢弃之前读取的所有内容。

      【讨论】:

      • 投反对票的人能否详细说明为什么他认为这个答案没有用?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多