【问题标题】:Loop until integer input is in required range fails to work with non-digit character inputs循环直到整数输入在所需范围内无法使用非数字字符输入
【发布时间】:2012-10-24 02:30:27
【问题描述】:

我对应该非常简单的代码有疑问。我想通过错误检查获取 1 到 3 之间的整数。它适用于检查太大或太小的数字,但是当输入字母/数字组合时,它会陷入无限循环。有什么建议吗?

#include <iostream>
using namespace std;

int main(int argc, char *argv[]){
    int input;

    cout << "\nPlease enter a number from 1 to 3:" << endl;
    cout << "-> ";
    cin >> input;

    while(input< 1 || input> 3){
        cout << "\n---------------------------------------" << endl;
        cout << "\n[!] The number you entered was invalid." << endl;
        cout << "\nPlease re-enter a number from 1 to 3" << endl;
        cout << "-> ";
        cin >> input;
    }

    cout << "You chose " << input << endl;
}

【问题讨论】:

标签: c++ error-handling integer infinite-loop


【解决方案1】:

问题是:

cin >> input;

当您尝试读取非数值时,将导致设置错误位。之后,任何使用 operator&gt;&gt; 的尝试都会被忽略。

因此,纠正此问题的方法是测试流是否处于良好状态,如果不是,则重置状态标志并再次尝试读取。但请注意,错误的输入(导致问题的)仍在输入上,因此您需要确保也将其丢弃。

if (cin >> input)
{
    // It worked (input is now in a good state)
}
else
{
    // input is in a bad state.
    // So first clear the state.
    cin.clear();

    // Now you must get rid of the bad input.
    // Personally I would just ignore the rest of the line
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    // now that you have reset the stream you can go back and try and read again.
}

为了防止它被卡住(这是由设置的坏位引起的)读入字符串,然后使用字符串流来解析用户输入。我也更喜欢这种方法(用于用户交互输入),因为它可以更轻松地组合不同的阅读风格(即结合operator&gt;&gt; 和std::getline(),因为您可以在字符串流上使用它们)。

#include <iostream>
#include <sstream>
#include <string>
// using namespace std;
// Try to stop using this.
// For anything other than a toy program it becomes a problem.

int main(int argc, char *argv[])
{
    int          input;

    std::string  line;
    while(std::getline(std::cin, line))   // read a line at a time for parsing.
    {
        std::stringstream linestream(line);
        if (!(linestream >> input))
        {
             // input was not a number
             // Error message and try again
             continue;
        }
        if ((input < 1) || (input > 3))
        {
             // Error out of range
             // Message and try again
             continue;
        }
        char errorTest;
        if (linestream >> errorTest)
        {
             // There was extra stuff on the same line.
             // ie sobody typed 2x<enter>
             // Error Message;
             continue;
        }

        // it worked perfectly.
        // The value is now in input.
        // So break out of the loop. 
        break;
    }
}

【讨论】:

  • @tuergeist:已修复。但你可以帮我修好。
  • @emileb:我不同意。阅读:Why is “using namespace std;” considered bad practice? using 构造在大多数情况下都是一个问题,并且在您维护难以发现和诊断的代码时会导致引入错误。
  • @LokiAstari 我知道这可能导致的问题,但如果您将“使用”内容保留在任何头文件之外,这些问题仅限于 cpp 源文件的内部.尽管如此,这可能会导致问题,但在 OP 的代码中,完全明确是没有用的。
  • @LokiAstari 另一件事是,例如using Foo::A; 然后添加using Bar::A,很容易发现您正在使两个具有相同名称的类在同一范围内可访问。大多数时候,您的using 行会在您的cpp 中的同一位置,从而使可能的错误易于修复。永远不要使用using namespace A; 和using namespace B;,像这样完全可访问的两个命名空间,你自找麻烦。我还是要告诉你,如果你不知道风险,最好避免。
  • @MikaelMello:谢谢。固定。
【解决方案2】:

这些答案中的大多数都包含不必要的复杂性。

输入验证是使用 do-while 的最佳时机

do{

   cout << "\nPlease enter a number from 1 to 3:" << endl;
   cout << "-> ";

   if(!cin){
      cout << "Invalid input"
      cin.clear()
      cin.ignore(numeric_limits<streamsize>::max(), '\n');
   }

}while(!(cin >> input))
  • 使用numeric_limits&lt;streamsize&gt;::max() 彻底清除 cin 失败后的缓冲区。

  • 使用cin.clear() 重置cin 上的失败标志,所以!cin 不会 总是评估为假。

cin.fail() 很好。但是有些人会认为!cin 更自然。

来自我之前的帖子https://stackoverflow.com/a/43421325/5890809

【讨论】:

    【解决方案3】:
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int validatedInput(int min = 1, int max = 3)
    {
        while(true)
        {
            cout << "Enter a number: ";
            string s;
            getline(cin,s);
            char *endp = 0;
            int ret = strtol(s.c_str(),&endp,10);
            if(endp!=s.c_str() && !*endp && ret >= min && ret <= max)
                return ret;
            cout << "Invalid input. Allowed range: " << min << "-" << max <<endl;
        }
    }
    
    int main(int argc, char *argv[])
    {
        int val = validatedInput();
        cout << "You entered " << val <<endl;
        return 0;
    }
    

    【讨论】:

    • 你忘记了#include &lt;stdlib.h&gt; strtol
    • 嗯,即使没有包含,我也会运行。但我认为这可能是他的最小问题;-)
    • strtol - 较新的编译器会告诉你这一点。
    【解决方案4】:

    您将输入声明为 int,但是当您编写字母数字字符作为输入时,它会尝试将其隐式转换为整数。但是您的错误检查并没有考虑到这一点。

    您的问题可以通过更改您的 while 循环轻松解决。而不是检查这个你如何检查

    while(input!=1 || input!=2 || input!=3)
    

    【讨论】:

    • 很遗憾没有。流处于错误状态。您需要在重试读取之前重置状态标志。
    • 如果我们必须检查输入是否在 1 到 12000 之间,或者只是验证输入以查看用户输入的是整数而不是字母数字乱码怎么办?
    猜你喜欢
    • 1970-01-01
    • 2021-09-09
    • 2020-08-15
    • 2023-04-02
    • 2012-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多