从STDIN读取int时的奇怪行为

时间:2012-03-19 23:54:21

标签: c++ menu stdin infinite-loop

假设我们有一个菜单向用户提供了一些选项:

Welcome:
1) Do something
2) Do something else
3) Do something cool
4) Quit

用户可以按1 - 4然后按回车键。程序执行此操作,然后将菜单返回给用户。无效选项应该只显示菜单。

我有以下main()方法:

int main()
{
    while (true)
        switch (menu())
        {
            case 1:
                doSomething();
                break;
            case 2:
                doSomethingElse();
                break;
            case 3:
                doSomethingCool();
                break;
            case 4:
                return 0;
            default:
                continue;
        }
}

以及menu()

int menu()
{
  cout << "Welcome:" << endl
       << "1: Do something" << endl
       << "2: Do something else" << endl
       << "3: Do something cool" << endl
       << "4: Quit" << endl;

  int result = 0;
  scanf("%d", &result);
  return result;
}

输入数字类型效果很好。输入1 - 4会使程序执行所需的操作,然后再次显示菜单。输入此范围之外的数字(例如-1或12)将再次按预期显示菜单。

但是,输入“q”之类的内容只会导致菜单无限次地反复显示,甚至无法停止获取用户输入。

我不明白这是怎么发生的。显然,正在调用menu(),因为菜单会一遍又一遍地显示,但是scanf()menu()的一部分,所以我不明白程序是如何进入此错误状态的不会提示用户输入。

我最初有cin >> result做了完全相同的事情。

修改:似乎有related question,但原始source code已从pastebin中消失,其中一个答案链接到article,显然曾经解释过为什么会这样,但现在已成为一个死链接。也许有人可以回复为什么会发生这种情况而不是链接? :)

修改:使用this example,以下是我解决问题的方法:

int getNumericalInput()
{
    string input = "";
    int result;

    while (true)
    {
        getline(cin, input);

        stringstream sStr(input);
        if (sStr >> result)
            return result;

        cout << "Invalid Input. Try again: ";
    }
}

我只是替换了

int result = 0;
scanf("%d", &result);

int result = getNumericalInput();

3 个答案:

答案 0 :(得分:6)

当您尝试将非数字输入转换为数字时,它会失败并且(重要部分)将该数据保留在输入缓冲区中,因此下次您尝试读取int时,它仍然在那里等待,并且再次失败 - 再一次,永远失败。

有两种基本方法可以避免这种情况。我更喜欢的是读取一串数据,然后从中转换为数字并采取适当的操作。通常,您将使用std::getline读取直到新行的所有数据,然后尝试转换它。因为它会读取正在等待的任何数据,所以你永远不会把垃圾“卡在”输入中。

替代方案是(特别是如果转换失败)使用std::ignore从输入读取数据(通常)到下一个换行符。

答案 1 :(得分:4)

1)对自己说1000次,或直到你入睡:


如果不检查返回值,我将永远不会使用I / O函数。


2)重复上述50次。

3)重新阅读您的代码:您是否正在检查scanf的结果?当scanf无法将输入转换为所需格式时会发生什么?如果您不了解这些信息,您将如何学习? (脑海中浮现出四个字母。)

我还会质疑为什么你使用scanf而不是更合适的iostreams操作,但这会遇到完全相同的问题。

答案 2 :(得分:1)

您需要验证读取是否成功。提示:它没有。在读取之后,请确保您已成功阅读输入:

if (std::cin >> result) { ... }
if (scanf("%d", result) == 1) { ... }

在C ++中,失败的状态是粘性的并且保持不变直到它被clear()编辑。只要流处于失败状态,它就不会做任何有用的事情。在任何一种情况下,您都想要ignore()坏人或fgetc()。请注意,失败可能是由于到达了流的末尾,在这种情况下设置了eof()或者分别为iostream或stdio返回了EOF