【问题标题】:Ignoring scanf value in while loop忽略while循环中的scanf值
【发布时间】:2021-10-17 19:10:19
【问题描述】:

我正在学习 C,但在运行此循环时遇到了问题。我写了一个while 循环来提示用户输入包裹和数量。我尝试验证要检查的数量的输入是否为整数(当用户键入字符时,它会提示用户再次键入)

第一次运行,一切都很好。

但是当循环第二次运行等时,我尝试输入一个字符作为包裹的数量,不会弹出消息告诉用户再次输入。

scanf 的值被忽略,tempQtty 的值等于用户之前键入的数量。

有没有办法解决这个问题,或者有其他方法可以验证用户输入是否为整数?

抱歉我的英文不好:")[输入、预期输入和实际输入][1]

while(skip != 'x')
        {
            printf("\n\n%27sPACKAGE A/B/C/D  ( x = skip ) : ", "");
            rewind(stdin);
            package = getchar();

            switch (package)
            {
            case'x':case'X': skip = tolower(package); break;
            case'A':case'a':case'B':case'b': case'C':case'c':case'D':case'd':
                printf("%27sQUANTITY%21s: ", "", "");
                rewind(stdin);
                scanf("%d", &tempQtty);   //here's the problem

                while (tempQtty < 0)
                {
                    printf("%27s(PLEASE KEY IN A CORRECT VALUE!)\n", "");
                    printf("%27sQUANTITY%21s: ", "", "");
                    rewind(stdin);
                    scanf("%d", &tempQtty);
                }
                switch (package)
                {
                case 'A':case 'a': qttyA = tempQtty; totalQttyA += tempQtty; break;
                case 'B':case 'b': qttyB = tempQtty; totalQttyB += tempQtty; break;
                case 'C':case 'c': qttyC = tempQtty; totalQttyC += tempQtty; break;
                case 'D':case 'd': qttyD = tempQtty; totalQttyD += tempQtty; break;
                    
                }
                break;
                default:
                    printf("%27s(NO SUCH PACKAGE! PLEASE KEY IN AGAIN!)\n", "");
            }
        }
printf("\nA = %d", qttyA);
printf("\nB = %d", qttyB);


  [1]: https://i.stack.imgur.com/hBD82.png

【问题讨论】:

  • 请给出准确的输入、预期结果和实际结果。如果你的意思是你输入一个需要整数的字符,那么这是行不通的。 scanf 会返回失败,输入数据不会被消费。您需要始终检查scanf 的返回值。此外,scanf 也不是很好,如果输入可能是错误的,因为不正确的输入不会被读取。而是使用 fgetssscanf
  • getchar 在做什么?
  • @DWei 这是一个令人惊讶的难题,尤其是如果您坚持使用scanf 进行输入。您必须做两件事: (1) 检查 scanf 的返回值:如果它没有返回 1,则表示它没有成功读取您要求它读取的值。 (2) 如果scanf 失败——如果它返回小于1——你必须以某种方式丢弃错误的输入。 (更多内容在下一条评论中。)
  • @DWei 所以你想在调用scanf 之后尝试“刷新”错误或未读的输入。在您遇到这个问题之前,很多很多人都遇到过这个问题,并且关于它的答案有很多很多。不幸的是,在所有这些答案中,我没有找到一个好的、“规范”的答案来指向你。但是,如果您搜索“scanf flush input”,您会找到答案——只需忽略任何建议使用fflush(stdin) 的内容,这是错误的。 (您也可以忽略所有解释为什么fflush(stdin) 是错误的,因为您已经知道了。寻找解释使用什么的答案。)
  • @DWei 但我有一个替代的,有点异端的建议给你。虽然尝试检查用户是否意外输入了“错误”输入,并尝试提示用户重试是一个崇高的目标,但事实证明,scanf 基本上没有这种用途 .使用scanf 编写好的代码来获取用户输入、检查其有效性并在必要时重新提示用户太难了。这太难了,我相信它不值得。有关详细信息,请参阅this answer

标签: c scanf


【解决方案1】:

首先,正如我在评论中提到的那样,这是一个令人惊讶的复杂问题,而且你并不孤单。这不是你的问题,也不是 C 语言的问题;这基本上只是 scanf 函数本身的问题。

部分答案:

(1) 到处都有类似的东西

scanf("%d", &tempQtty);
while (tempQtty < 0)
    ...

这是错误的。如果您要求使用%d 进行整数输入,并且如果用户键入了非数字的内容,那么scanf 所做的not 所做的是将tempQtty 填写为-1。它所做 所做的是返回一个值,表示它无法转换您(程序员)要求的内容。所以你想把它改成更像

while (scanf("%d", &tempQtty) != 1)
    ...

(2) 如果用户没有输入您请求的整数,并且如果scanf 返回 0 来告诉您,则存在问题:用户输入的非数字输入可能仍在输入流上。看起来您可能已经意识到这一点,并且您正试图通过调用rewind(stdin) 来消除未读输入。但这行不通;这不是这样做的方法。

你想做的是写一个像这样的小“帮助”函数:

void flush_unread_input()
{
    int c;
    do {
        c = getchar();
    } while(c != EOF && c != '\n');
}

然后,无论您在哪里检测到错误(即,无论在哪里 scanf 返回的不是 1),都不要调用 rewind(stdin),而只需调用 flush_unread_input()

还有几点:

  • 您可能需要尝试在哪里拨打flush_unread_input 以及在哪里不拨打。您不能盲目地将它撒在各处,因为它基本上会读取并丢弃该行的其余部分,但这意味着它也可以读取并丢弃整行,这有时可能是您真正想要的一行。
  • 有很多方法可以编写flush_unread_input 函数。我已经展示了一种应该很容易理解的方式,但是您经常会看到更紧凑的方式,例如while((c = getchar()) != EOF &amp;&amp; c != '\n');。 (我还没有测试我在这里展示的版本。)
  • 我的回答可能暗示scanf 如果成功则返回 1,如果失败则返回 0,但它比这更复杂。 scanf 实际上返回成功转换和存储的项目数,如果您的格式说明符包含多个 % 符号,则可能大于 1。

关于这个话题还有很多话要说,但今天早上我没有时间写更长的答案。

【讨论】:

    【解决方案2】:

    我尝试输入一个字符作为包裹的数量,不会弹出提示用户再次输入的信息。

    scanf 的值被忽略,tempQtty 的值等于用户之前键入的数量。

    有没有办法解决这个问题,或者有其他方法可以验证用户输入是否为整数?

    如果您输入z,您的假设tempQtty 将收到负值是没有根据的。事实上,C 标准规定如果转换失败,tempQtty 不受影响。 解决这个问题的方法忽略scanf返回值,它告诉输入是否有效。

    【讨论】:

      猜你喜欢
      • 2011-07-14
      • 2016-09-08
      • 1970-01-01
      • 2017-04-03
      • 2017-04-29
      • 1970-01-01
      • 2016-12-24
      • 2013-08-04
      • 2019-11-07
      相关资源
      最近更新 更多