【问题标题】:Error receiving inputs接收输入错误
【发布时间】:2012-01-10 03:03:33
【问题描述】:

我经常看到在要求输入的一系列 printf 语句中,某些情况 语句不接受输入,即

printf_statement1:

printf_statement2: /*Enter input here*/

即在用户输入 statement1 的输入之前,会打印 statement2,允许用户输入输入,然后对 statement3 重复相同的操作& statement4.

是什么促成了这种情况?

我遇到过这个,但现在没有了,在:

 printf("\n\n\t\tName: ");
 scanf("%s", name); //initially used gets
 printf("\n\n\t\tType: ");     
 scanf("%c", &type);
 printf("\n\n\t\tAddress: ");      //initially used gets
 scanf("%s", address);
 printf("\n\n\t\tDate - of - Birth: ");
 scanf("%s", dob); //initially used gets

输出:

Name:
Type: I enter something!
Address:
Date - of - Birth: I enter something!

【问题讨论】:

  • 澄清一下:您是说您之前执行了给定的代码,实际上它并没有提示您输入名称或地址,但现在它可以了吗?
  • 是的。想知道原因。

标签: c


【解决方案1】:

我知道这个问题已被标记为已回答,但由于 fflush(stdin) 让我畏缩,我想发布一个替代清除 stdin 的方法:

void clear_stdin(void)
{
   int c;
   do {
      c = fgetc(stdin);
   } while (c != '\n' && c != EOF);
}

这将清除所有chars,直到出现新行或遇到EOF。 (您可以将 do..while 的主体和条件粘贴到 while 循环的条件中,但我更喜欢这样写。)

例如在想要读取单个 char 之后调用该函数会很有用,以防止在用户输入超过 1 个字符时似乎跳过后续提示。

还可以考虑使用fgets() 或使用scanf() 提供宽度说明符以防止溢出。

【讨论】:

    【解决方案2】:

    要替换“Jonathon Reinhart”答案中的 fflush(stdin),我建议使用此功能,该功能符合标准:

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

    【讨论】:

    • 我的回答怎么不“符合标准”?我没有提供足够的文件吗?
    • 根据标准,fflush(stdin) 的行为是未定义的。那就是我的意思。因此必须小心使用或不使用它以避免将来可能出现的错误,因为您不知道将来可以对您的代码做什么。但是,当然,您提供了许多有价值的信息。 (我投票赞成你的详细回答)
    【解决方案3】:

    问题的根源在于"%c" 转换说明符没有跳过空格。假设您输入“Joe”作为名称;输入流将包含字符“J”、“o”、“e”、“\n”。第一次调用scanf 查找字符串("%s" 转换说明符);它将消耗“J”、“o”和“e”,并将“\n”留在输入流中。为响应下一个提示,您键入一个类似“A”的字符,因此输入流现在包含“\n”、“A”、“\n”。对scanf 的下一次调用会查找单个字符;它将消耗第一个换行符并返回,将 'A' 和 '\n' 留在输入流中。

    与其刷新stdin(这不是根据语言定义明确定义的操作),不如避免使用"%c" 转换说明符进行交互式输入;坦率地说,最好避免使用scanf 进行交互式输入,而是使用fgets。

    一种快速而肮脏的解决方案是将type 的类型从简单的char 更改为char 的数组并将其作为字符串读取:

    char type[2];
    ...
    fgets(type, sizeof type, stdin);
    

    这将跳过卡在输入流中的任何换行符,而无需刷新任何内容。然后,您只需使用 type[0] 作为您的类型值。

    编辑

    如果你不想改变type的类型,你可以这样做:

    #include <ctype.h>
    ...
    while ((type = fgetc(stdin)) && isspace(type))
      ;
    

    它将从stdin 中读取单个字符,直到它看到一个非空白字符。

    【讨论】:

      【解决方案4】:

      有时在这种情况下,在阅读之前在stdin 上调用fflush() 会很有帮助:

      char name[40+1];
      printf("\n\tName: ");
      fflush(stdin);
      scanf("%40s", name);
      

      根据具体情况,输入缓冲区中可能有一个换行符,导致您的第一个 scanf 被跳过(在没有输入的情况下)。

      另外请注意,您应该在scanf 中指定缓冲区的大小以避免buffer overflows。

      scanf 的格式说明符如下所示:

      %[*][width][modifiers]type
      

      width:指定当前读取操作中要读取的最大字符数

      因此,%40s 指定只能将 40 个字符读入字符串。请注意,缓冲区是 41 个字符,为 NULL 终止符留出空间。

      编辑:一些消息来源(如this 和this)说你应该避免调用fflush(stdin),这是不正确的。 然而,Linux man page 声明:

      对于输入流,fflush() 会丢弃任何已从底层文件获取但尚未被应用程序使用的缓冲数据。

      符合 C89、C99、POSIX.1-2001、POSIX.1-2008。 标准没有指定输入流的行为。大多数其他实现的行为与 Linux 相同。

      另外:MSDN 没有提及调用fflush(stdin),除了以下内容:

      如果流打开输入,fflush 清除缓冲区的内容。

      人们会认为,如果调用 fflush(stdin) 是个问题,fflush 上两个最大的文档来源之一就会提到这一点。

      【讨论】:

      • 你怎么称呼“%40s”?任何资源都可能有帮助。
      • 如何刷新标准输入 - 你的意思是标准输出吗?
      • 据我了解,他的scanfs 没有获得输入数据。如果 input 缓冲区中有换行符,则可能是这种情况。
      • @JonathonReinhart:我将遵循 C 语言标准。 7.19.5.2 - "2 如果stream 指向未输入最新操作的输出流或更新流,fflush 函数会导致该流的任何未写入数据被传递到主机环境写入文件;否则,行为未定义。“强调我的。
      • 手册页确实提到了它;它告诉您“标准没有指定输入流的行为”。调用fflush(stdin) 会使您的代码不可移植——并且有完全可移植的方法来做同样的事情。如果您没有看到任何异常,可能是因为您没有使用 fflush(stdin) 行为不端的系统;你能确定你的代码永远不会在这样的系统上运行吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多