【问题标题】:scanf gets skipped, even with safeties (getchar())scanf 被跳过,即使有安全措施 (getchar())
【发布时间】:2013-08-04 02:41:35
【问题描述】:

我知道这个问题被问了一百遍,我已经搜索了所有的可能性,但我想我还不够熟练,无法知道这个问题出在哪里。我正在编写一个程序,我需要用数据(整数和字符串)填充结构。我第一次尝试它时,它跳过了除第一个之外的所有内容,但我没有惊慌,因为我记得在课堂上我需要使用fflush(stdin) 来克服这个问题。我搜索过的网站投票反对使用fflush(stdin),因为它具有未定义的行为。他们说使用getchar() 会吃掉额外的换行符,从而解决问题。因此我的代码:

int manNode(){
Item *p;
int helper;
p = (Item*)malloc(sizeof(Item));
printf("Welk type? (Taak:1, Examen:2, Voordracht:3)\n");
scanf("%u",&helper);                           //selecting an itemtype
if (helper < 1 || helper > 3)
{
    printf("wrong value, please try again");
    return 0;
}
getchar();                                     //I've just put getchars everywhere for safety.
p->entrytype = helper-1;
helper = 0;
printf("Vul een naam in:\n");
scanf("%s", p->name);                          //this one fills in fine
getchar();
printf("Vul een vaknaam in: \n");
scanf("%s", p->course);                        //this one gets skipped if I type more than one letter in the last scanf()                
getchar();
printf("Vul een starttijd in:\n");             //From here on out everything gets skipped
p->start = getTijd();
checkTijd(p->start);                           
printf("Vul een eindtijd in: \n");
p->end = getTijd();
checkTijd(p->end);

我知道这有点混乱,但请关注 scanfs 和 getchars。 getTijd() 也有几个 scanfs 用于扫描整数,它们也会被跳过。我不知道从这里去哪里。 (代码并非不完整,其余无关紧要)

【问题讨论】:

  • 你能展示一下Item的结构吗?
  • scanf 大约是 C 语言中可用的最糟糕的解析工具,因为它尝试执行两项任务(读取输入并将其分成几部分),当这些部分不完全符合预期时,它会离开输入流处于难以确定的状态。 fgets/sscanf 使分工更加稳健。
  • 每次使用时都必须检查scanf()是否成功。当它出错时,它仍然是错误的。您应该错误检查getchar() 函数。您还应该通过打印刚刚读取的值来进行调试,以确保您得到了您认为得到的结果。
  • 请注意,fflush(stdin) 是在 Windows 上定义的,而不是在 Unix 平台上定义的。请研究如何创建 SSCCE (Short, Self-Contained, Correct Example)。此外,您应该真正展示一个运行程序时所做的示例,以便我们可以看到出了什么问题。
  • 使用 fflush(stdout) 并按回车键强制标准输入缓冲区由 scanf 处理。请参阅下面的答案。

标签: c input stdin scanf fflush


【解决方案1】:

你可以定义一个新的getchar

#include <stdlib.h>
#include <stdio.h>

#define getchar(x) (scanf("%c", x))

int main ()
{
    char x, y[10];
    getchar(&x);
    scanf("%s", y);
    printf("got %s\n", y);
    return 0;
}

更新:这可能是更好的方法

#include <stdlib.h>
#include <stdio.h>

void work_to_do()
{
#define getchar(x) (scanf("%c", x))
    char x, y[10];
    getchar(&x);
    scanf("%s", y);
    printf("got %s\n", y);
#undef getchar
}
int main ()
{
    work_to_do();
    return 0;
}

解决 scanf 换行忽略和 getchar (但仍然 scanf 忽略空格)

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

#define __getchar() (read(2, NULL, 1)) // 2 stands for standard error, we can make the user enter a character.

void work_to_do()
{   
    char y[10];
    __getchar();
    scanf("%s", y);
    printf("got %s\n", y);
    __getchar();
}
int main ()
{
    work_to_do();
    return 0;
}

看看这个很好: Read space-separated values from file

【讨论】:

  • 至少,在重新定义它之前,你必须#undef getchar。我不相信这是个好主意。最好为宏使用替代名称(可能是GetChar)。
  • 问题是&lt;stdio.h&gt;通常会定义一个宏getchar()所以在你自己做#define getchar之前,你应该#undef getchar取消标准版本。
  • 就我而言,我认为这不是真的,编译器不应该警告我重新定义吗?
  • 虽然我认为 Ghijs 的问题仍然存在,但导致 scanf 忽略换行符...
【解决方案2】:

scanf 按文档说明工作。还有,scanf只要能理解它的功能就没有问题。

我创建了您的代码副本并对其进行提炼以突出显示您想要做的事情。其余部分您需要自己填写。

在此代码中,您需要输入一个数字并按 Enter。然后输入一个字符串并按回车等。注意。 fflush(stdout) 语句是每个 K&R 标准 C 实现的一部分。 fflush 将缓冲区的内容强制输出到控制台。这种刷新有助于合理的用户/计算机对话。

#include <stdio.h>
main()
{
    char string[100];
    int  helper;

    printf("Welk type? (Taak:1, Examen:2, Voordracht:3)\n");
    fflush(stdout);

    scanf("%d",&helper);    //select an itemtype
    if (helper < 1 || helper > 3)
    {
        printf("wrong value, please try again");
        return 0;
    }

    printf("Vul een naam in:\n");
    fflush(stdout);

    scanf("%s", &string[0]);
    printf("\n%s\n", string);

    printf("Vul een vaknaam in: \n");
    fflush(stdout);

    scanf("%s", &string[0]);
    printf("\n%s\n", string);

    printf("Vul een starttijd in:\n");
    fflush(stdout);
    scanf("%s", &string[0]);
    printf("\n%s\n", string);

    printf("Vul een eindtijd in: \n");
    fflush(stdout);
    scanf("%s", &string[0]);
    printf("\n%s\n", string);
}

此代码使用 Microsoft C 编译器在 Eclipse 上运行。

另外,让我强调一下,如果您输入:1 AAA BBB CCC DDD 并按回车,那么scanf 将被执行五次。在此示例中,代码将运行完成!

所以你需要跟踪你的代码逻辑(在本例中是一条直线),以查看如何根据输入的数据调用 scanfs。

希望这会有所帮助。如有更多问题,请询问。

【讨论】:

  • 这对于琐碎的输入是错误的。如果您以任意数量的 Return 回答“Vul enn naam in:”,则 scanf 永远不会返回。
猜你喜欢
  • 1970-01-01
  • 2015-01-30
  • 1970-01-01
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多