Do..While 循环不会停止等待 uinput

Do..While loop doesn't stop to wait for uinput

我正在使用基本的输入有效性检查机制实现一个超级简单的菜单选择过程。合法输入是 {1,2,3} 因此可能的问题是数字超出此范围或非整数。我的代码如下所示。这适用于前一个问题(即当我输入“4”时)但对于后者(当我尝试输入一个字符时),它一遍又一遍地打印无效消息而不是等待新的输入,就像它除了第一次之外,每次迭代都完全跳过 scanf 行。我在这里错过了什么?

do{
      try = scanf("%d", &selection);
      if(try!=1 || selection < 1 || selection > 3){
           printf("\nInvalid input. Dear guest, please enter '1', '2', or '3'.\n\nInput:");
      }
 }while(try!=1 || selection < 1 || selection > 3);

输入一个不能作为十进制数文本表示的一部分的字符后,比如 'A',程序可用的输入是字节序列 'A' '\n'(后者是换行符),或者 Windows 上的 'A' '\r' '\n' (回车符 return 后跟换行符)。

当 scanf 试图从这些字符中解析一个数字时,它已经在 'A' 处犹豫不决并将其放回输入流中。 C 中的输入流保证您可以执行至少一个 ungetc(),即至少将一个字符放回流中,使其成为下一个输入操作读取的第一个字符。这个简单但巧妙的工具使它 很多 更容易处理 variable-format 输入:想象一下你在一些 C 源代码中解析一个表达式,并且整数文字或变量名在语法上都是允许作为下一个标记:您可以先尝试数字,如果失败,输入仍然包含要处理的所有变量名称。将第一个失败字符“牢记”并使其可用于程序的其他部分的工作封装在 FILE 实现中。

这就是这里发生的事情。第一次失败的 scanf() 将 'A' 放回去,以便下一次尝试再次遇到它,ad infinitum。 必须从 'A' 中删除输入。更具体地说,应该从输入中完全删除下一个“单词”:用户可能输入了“kkjkllkjlk”,而您不希望出现 10 条错误消息。您可以决定是否接受“lklkj2”(并阅读第 2 个),但丢弃整个单词更简单。

您还可以决定是否接受“1 2 3 2”作为 4 个有效的连续输入,或者是否要求在数字之间换行;出于一般性考虑(例如,如果输入不是来自终端),我会接受所有由任何类型的空格分隔的数字序列。在这种情况下,您只想阅读下一个空格:

#include <ctype.h>
// ...
while(!isspace(getchar())) { /* ignore */ }

这应该可以解决问题。这一个后面可能有更多的空格,包括换行符等,但这没关系:scanf 的符号输入转换(如 %d)跳过前导空格。

我认为让用户通过结束输入退出程序(在 Windows 控制台中按 Ctrl-z 插入 end-of-file,或 Ctrl-d在 Posix 终端上),所以我会测试 scanf() returning EOF 的特殊情况。如果输入来自可能实际上必不可少的管道。即使在丢弃错误输入的代码中(Posix:echo -n "a" | myprog 会挂起;-n 会抑制 echo 通常附加)。综上所述,我对输入循环的看法是:

while(1) { // break on good input
      printf("Please enter your choice of 1, 2 or 3:\n");
      try = scanf("%d", &selection);
      if(try!=1 || selection < 1 || selection > 3){
          if(try == EOF)
          {
              return 0;
          }
           printf("\nThe input was not 1,2 or 3. Please try again.\n");
           int discard;
           {
               do{
                   discard = getchar();
                   if(discard == EOF) { return 0;} // catches EOF after bad char
               }while(!isspace(discard));
           }
      }
      else break;
}