c++中while循环的意外行为

Posted

技术标签:

【中文标题】c++中while循环的意外行为【英文标题】:Unexpected behavior of while loop in c++ 【发布时间】:2018-09-23 17:06:34 【问题描述】:

做了这样的事情:

int main()

    while (true)
    
        std::cout << "Enter a number between one and nine. \n";
        int oneandnine;
        std::cin >> oneandnine;
        if (std::cin.fail())
        
            std::cin.clear();
            std::cin.ignore(100, '\n');
            std::cout << "INVALID UNPUT!\n";
        else if (oneandnine <= 9 &&  oneandnine >= 1)
        
            break;
        else
        
           std::cout << "INVALID UNPUT!\n";
        
    

    return 0;

当提供类似456aihdb 的输入时,得到类似这样的信息:

  INVALID UNPUT!
  Enter a number between one and nine.
  INVALID UNPUT!
  Enter a number between one and nine.

为什么会这样循环两次?是因为当456 被丢弃而其余的aihdb 没有导致它再次循环并跳过cin 输入时?

【问题讨论】:

如果您也在寻找解决此类问题的方法,请将数据读入字符串,然后您自己将字符串转换为整数。这样,您就可以完全控制输入的测试有效性,而不是让“系统”来进行这些检查。 【参考方案1】:

和你想的完全一样。

fail 标志不会立即设置,而是格式化输入运算符将整数 456 读入 oneandnine,但不会设置 fail 标志,因为它是一个有效的整数值。这会导致 else 案例执行,因为 std::cin.fail() 为 false 并且 oneandnine 不在 19 之间。

您读取无效输入的下一次迭代和fail 标志将被设置,导致第二个错误输出。


处理验证的一种常见方法是将整行读入一个字符串,将该字符串放入一个std::istringstream 并使用它来尝试解析输入:

if (!std::getline(std::cin, line))

    // Failure of some kind, could be EOF or something else
    // Probably best not to continue in this case


std::istringstream iss(line);

if (!(iss >> oneandnine))

    // Invalid input, report it as such


if (oneandnine < 1 || oneandnine > 9)

    // Invalid number, report it as such


// Correct input, continue with program

请注意,6abc 等输入将被上述代码视为有效。 6 将被提取到 oneandnine 中,abc 部分将被静默丢弃。如果不需要,还有其他解析方式(例如,std::stoistd::strtol 如果不需要异常)。这样做而不是 &gt;&gt; 提取,但上面的其余代码应该没问题。

【讨论】:

std::istringstream 读取有完全相同的问题,除非您检查读取操作是否消耗了整个流 @AlanBirtles 不,不是。因为getline 调用已经使用了用户输入,这意味着此类输入将被静默忽略。 我想 OP 希望整个输入都是有效的,而不是默默地忽略无效输入【参考方案2】:

std::istreamoperator &gt;&gt; 不会读取整行。它读取直到找到无效字符或空格,如果在无效字符之前找到有效字符,则读取操作成功并且无效字符留在流中。

在您的示例中,第一次迭代成功读取 456 并将 aihdb 留在流中。这使您的范围检查失败,然后第二次迭代尝试读取剩余的字符,但由于第一个字符不是数字而失败。

如果您想阅读整行,请使用std::getline,然后将整行解析为一个数字。例如:

#include <iostream>
#include <string>

using std::cout;

int main()

    while (true)
    
        std::cout << "Enter a number between one and nine. \n";
        std::string line;
        std::getline(std::cin, line);
        int oneandnine;
        size_t pos;
        try
        
            oneandnine = std::stoi(line, &pos);
        
        catch ( std::exception& )
        
           oneandnine = -1;
        
        if (pos != line.size() || oneandnine > 9 || oneandnine < 1)
        
            std::cout << "INVALID INPUT!\n";
        
        else
        
            break;
        
    

    return 0;

【讨论】:

std::istream 没有读完整行 没有多大意义。鉴于上下文,您的意思是 Stream 运算符 &gt;&gt; 不会读取整行 @user4581301 operator&gt;&gt;std::istream 的一部分:en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt std::istream::getline也是如此。

以上是关于c++中while循环的意外行为的主要内容,如果未能解决你的问题,请参考以下文章

C++ while循环奇怪的行为

do…while循环中无法理解的意外令牌错误

c++用while循环结构,输出1-10中所有的奇数

矩阵形成中的奇怪行为(C++,犰狳)

C++中do while()的用法

在while循环中继续的奇怪行为