【问题标题】:Using scanf and fgets in the same program?在同一个程序中使用 scanf 和 fgets?
【发布时间】:2011-07-29 15:31:05
【问题描述】:

我需要执行以下操作:

int main(void) {
char a,b,cstring;

printf("please enter something");
scanf("%c %c",&a,&b);
prinf("thanks, now some more");
fgets(cstring,35,stdin);

}

问题是每当我从 scanf 输入第一个变量时,它都会跳到程序的末尾。如何进行多个输入?

【问题讨论】:

    标签: c scanf


    【解决方案1】:

    第一个问题是scanf() 读取两个字符,而不是之后的换行符。

    这意味着您的 fgets() 读取换行符并完成。

    你很幸运你的程序没有崩溃。您告诉fgets(),它正在获取一个包含 35 个字符的数组,但不是传递数组,而是传递字符(甚至不是字符的地址)。这也告诉我们你没有包括#include <stdio.h>(根据C标准,如果没有scanf()的原型,你就不能可靠地使用scanf()),并且你没有编译启用了足够多的警告,或者没有足够注意编译器告诉您的内容。

    您应该收到编译器警告。例如,GCC 4.6.0 说:

    /usr/bin/gcc -g -I/Users/jleffler/inc -std=c99 -Wall -Wextra -Wmissing-prototypes \
         -Wstrict-prototypes -Wold-style-definition xxx.c
    xxx.c: In function ‘main’:
    xxx.c:7: warning: implicit declaration of function ‘prinf’
    xxx.c:8: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast
    

    而且,该死,我什至没有注意到 prinf()printf() 的拼写错误,直到我尝试链接并且编译器揉了揉我的鼻子说“不知道 prinf() 是什么! ”。

    如果你的编译器至少没有给出第二个警告,那就给自己找一个更好的编译器。


    评论:

    如何阻止它读取之后的换行符?

    问题是没有阻止scanf() 读取换行符;问题实际上是如何让它读取它,以便fgets() 在下一行输入中获得清晰的运行。这实际上有点棘手——我很少使用scanf()(但我经常使用sscanf())的原因之一。这可以完成简单输入的工作:

    #include <stdio.h>
    int main(void)
    {
        char a,b,cstring[35];
    
        printf("please enter something");
        scanf("%c %c%*c", &a, &b);
        printf("thanks, now some more");
        fgets(cstring, 35, stdin);
        printf("OK: I got %c and %c and <<%s>>\n", a, b, cstring);
        return 0;
    }
    

    %*c 读取了一个额外的字符(换行符)而没有在任何地方分配它(这是因为*)。但是,如果在第二个非空白之后有多个字符,则无济于事。我可能会写一个循环来读取换行符:

    #include <stdio.h>
    int main(void)
    {
        char a,b,cstring[35];
        int c;
    
        printf("please enter something");
        scanf("%c %c", &a, &b);
        while ((c = getchar()) != EOF && c != '\n')
            ;
        printf("thanks, now some more");
        fgets(cstring, 35, stdin);
        printf("OK: I got %c and %c and <<%s>>\n", a, b, cstring);
        return 0;
    }
    

    请注意,getchar() 返回的是 int,而不是 char。这可靠地丢弃了第一行输入的所有内容。它还展示了如何回显您阅读的内容 - 调试的重要部分。

    【讨论】:

    • 对不起,我正在用 stdio 编译。如何阻止它读取之后的换行符?
    • 哦,我明白了。非常感谢您花时间解释这一点。
    • @Wes:注意我的代码没有检查scanf()是否成功读取了两个字符;这应该。它还应该检查fgets() 是否也有输入。但是,这会使答案略微混乱,让您更难于只见树木不见森林。
    【解决方案2】:

    这是未定义的行为。您的cstring 变量,尽管它的名字,是一个单一的字符,,并且您正试图将最多35 个字符读入它的当前值指向。换句话说,cstring 中的当前字符将被转换为一个指针,它会尝试在那里写入您的输入。这将是一个介于 0 到 255 之间的内存位置(很可能)。

    这就是导致您的分段违规的原因。从这样的开始:

    #include <stdio.h>
    int main(void) {
        char a,b,cstring[99];
    
        printf("please enter something");
        scanf("%c %c",&a,&b);
        printf("thanks, now some more");
        fgets(cstring,35,stdin);
    
        return 0;
    }
    

    或者,可能更好:

    #include <stdio.h>
    int main(void) {
        char a,b,cstring[99];
    
        printf("please enter something");
        fgets(cstring,35,stdin);
        sscanf(cstring,"%c %c",&a,&b);
        printf("thanks, now some more");
        fgets(cstring,35,stdin);
    
        return 0;
    }
    

    或者,如果您想要更强大的用户输入解决方案,请参阅here。这是一种久经考验的方法,具有非常好的错误检查功能。

    【讨论】:

    • 啊,应该是字符串类型吧?
    • 或者我应该将其定义为 char cstring[35] 吗?
    • @Wes:是的,看到更新,你基本上需要创建一个足够大的数组来保存你要输入的数据。
    • @Wes,C中没有字符串类型,c字符串只是一个以null结尾的字符数组
    • 那是因为你的 scanf 得到 两个 字符,但换行符仍在缓冲区中。这意味着 fgets 会得到它。您可以在 scanf 之后使用 getchar 将其删除,但您仍然会被边缘情况所吸引。说真的,点击链接并使用一个像样的输入功能。
    【解决方案3】:

    问题是当流到达 fgets() 时,这个 API 首先查看 stdin 的输入,并在那里找到一个杂散的 '\n'。至于 fgets() 表示这个 API 在遇到 EOF 或 '\n' 时返回,因为 fgets() 从 stdin 得到了 '\n',所以它在 API 返回条件满足时无需等待用户输入内容即可执行。

    问题的解决方法是你使用getchar();在 scanf 之后并忽略 scanf() 留下的多余字符。 例如:

    scanf("%lf",&e);
    getchar();
    fgets(t,200,stdin);
    

    其他解决方案可以将 sscanf 与 fgets 一起使用。 例如:

    #include <stdio.h>
    int main() {
    int j;char t[200];
    fgets(t,200,stdin);
    sscanf(t,"%d",&j);
    }
    

    【讨论】:

      【解决方案4】:

      您可以在使用 scanf 后使用 fflush(stdin) 来解决问题

      【讨论】:

      • fflush 是一个输出操作(它将缓冲数据写入底层流)。在stdin 等输入句柄上使用它具有未定义的行为。
      • 参见Using fflush(stdin),详细讨论使用fflush(stdin)的优点和其他方面。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-24
      相关资源
      最近更新 更多