【问题标题】:fgets doesn't work after scanf [duplicate]scanf后fgets不起作用[重复]
【发布时间】:2021-12-17 07:40:54
【问题描述】:
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void delspace(char *str);

int main() {
    int i, loops;
    char s1[101], s2[101];

    scanf("%d", &loops);

    while (loops--) {
        fgets(s1, 101, stdin);
        fgets(s2, 101, stdin);
        s1[strlen(s1)] = '\0';
        s2[strlen(s2)] = '\0';

        if (s1[0] == '\n' && s2[0] == '\n') {
            printf("YES\n");
            continue;
        }

        delspace(s1);
        delspace(s2);

        for (i = 0; s1[i] != '\0'; i++)
            s1[i] = tolower(s1[i]);

        for (i = 0; s2[i] != '\0'; i++)
            s2[i] = tolower(s2[i]);

        if (strcmp(s1, s2) == 0) {
            printf("YES\n");
        }
        else {
            printf("NO\n");
        }
    }

    return 0;
}

void delspace(char* str) {
    int i = 0;
    int j = 0;
    char sTmp[strlen(str)];

    while (str[i++] != '\0') {
        if (str[i] != ' ') {
            sTmp[j++] = str[i];
        }
    }
    sTmp[j] = '\0';
    strcpy(str, sTmp);
}

在我输入“loops”后,“s1”被自动分配了一个空行。它是如何发生的?我确定我的键盘工作正常。

【问题讨论】:

  • “我确定我的键盘工作正常。”大声笑!
  • 这个问题的一些变体似乎每天都在这里被问到。
  • 函数delspace肯定是错的!这会将abc\0 转换为bc\0。
  • 经验法则:对于交互式输入,请始终阅读 lines。

标签: c scanf fgets


【解决方案1】:

scanf() 准确地读取您要求的内容,将以下 \n 从该行的末尾留在缓冲区中,fgets() 将读取它。要么做一些事情来消耗换行符,要么(我的首选解决方案)fgets() 然后sscanf() 来自该字符串。

【讨论】:

  • 如果我想使用scanf(),我该如何消费新行?谢谢。
  • 在格式末尾使用%*[^\n]%*c 之类的东西来跳过换行符上的任何字符,然后是换行符本身。
  • @geekosaur:如果在读取的前一个内容之后和换行符之前没有字符,那么将%*[^\n]%*c 附加到格式的末尾实际上是行不通的,那么%*[^\n] 将匹配失败,因此%*c 将被跳过,换行符仍将留在输入中。您需要在单独的 scanf 调用中执行 %*c 才能使其工作。
【解决方案2】:

scanf 在输入缓冲区中留下空白,包括换行符。要使用 fgets 读取下一行,您需要手动删除当前行的其余部分:

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

【讨论】:

  • 是的,这也可以,但我个人只使用 fgets 然后 sscanf (如 geekosaur 所建议的那样),因为它对各种数据“统一”工作......而不仅仅是单个字符。我喜欢“通用的、多用途的解决方案”。干杯。基思。
  • 这确实比一个字符更有效 - 这就是循环的用途;)
  • while(c != EOF &amp;&amp; c != '\n'); 不好。 c 超出范围。
  • 我本来想原谅自己说我一定是混合了 C 和 Lua 范围规则,但显然这个答案是在我学习 Lua 之前,哈哈哈。
【解决方案3】:

这是一个更简单的解决方案

scanf("%d",&loops);
while ((getchar()) != '\n'); //This will consume the '\n' char
//now you're free to use fgets
fgets(string,sizeof(string),stdin);

【讨论】:

    【解决方案4】:

    维恩,

    Geekoaur 已经很好地回答了您的问题,我只是指出了您的代码的另一个“问题”。

    s1[strlen(s1)] = '\0'; 行是 no-op 如果 s1 在执行之前已经正确地以空值终止。

    但是,如果s1 在此行执行之前还没有正确地以空值终止(而且你很不幸),它将导致:

    • POSIX (*nix) 系统上的SIGSEGV。
    • Windows 上的GPF。

    这是因为strlen 基本上找到了现有的空终止符 的索引并返回它!下面是一个有效的、未优化的 strlen 实现:

    int strlen(char *string) {
        int i = 0;
        while(string[i] != '\0') {
            ++i;
        }
        return i;
    }
    

    所以...如果您真的担心字符串不会以 null 结尾,那么您会执行以下操作:

    • string[sizeof(string)]='\0'; 在 local 自动字符串上(编译器“知道”字符串的大小);
    • 或 string[SIZE_OF_STRING] 用于所有其他字符串,其中 SIZE_OF_STRING 是(最常见的)#define'd 常量,或者是您专门维护的变量,用于存储动态分配的当前大小(而不是长度)字符串。

    如果你真的、真的、真的担心字符串不是以 null 结尾的(就像你正在处理“脏”的库方法(比如 Tuxedo 的 ATMI)你ALSO)在将它们传递给可疑库方法之前清除“您的“返回字符串”:

    • 之前:memset(string, NULL, SIZE_OF_STRING);
    • 调用:DirtyFunction(/*out*/string);
    • 之后:string[SIZE_OF_STRING]='\0'

    SIG11 是一个完整的 biatch,因为(除非你用 signal-processor “挂钩”它们,否则它们会导致 unix 硬终止你的程序,所以你不能记录任何东西(事后)帮助找出到底是从哪里来的......特别是考虑到在许多情况下,抛出 SIG11 的代码行与字符串丢失的实际原因不相上下,它是空的-终结者。

    这对你有意义吗?

    干杯,伙计。基思。

    PS:警告:strncpy 并不总是以 null 终止...您可能指的是 strlcpy。我很难学到这一点……当一次 6000 万美元的账单崩溃时。


    编辑:

    仅供参考:这是 strlen 的“安全”(未优化)版本,我将其称为 strnlen(我认为这应该在 stdlib 中。叹息。)。

    // retuns the length of the string (capped at size-1)
    int strnlen(char *string, int size) {
        int i = 0;
        while( i<size && string[i]!='\0' ) {
            ++i;
        }
        return i;
    }
    

    【讨论】:

    • 我是 C 新手,教科书上并没有过多讨论字符串和安全性。也许我的教科书不够好:(谢谢你的专业建议:)
    • 不要做string[sizeof(string)]='\0'!这将在分配的内存之后写入\0! sizeof(s1) 是 101,s1 的索引从 0 到 100! strnlen 也会出现同样的问题。如果没有\0,那么它将返回size,而不是评论中提到的size - 1!
    【解决方案5】:

    我知道这已经很老了。我是 c 新手,想检查我的方法,它使用 getchar:

    #include <stdio.h>
    
    int main()
    {
    
        printf("Please enter your name\n");
        char string[10];
    
        scanf("%s", string);
        printf("Hello %s\n", string);
    
        //getchar();  # un commenting this line, fgets perfectly works!!
        printf("Please enter your name again\n");
    
        fgets ( string, 10, stdin );     
    
        printf("Hello again %s", string);
    
        getchar();
    }
    

    【讨论】:

    • 尝试输入"Michele \n"(名称后的空格)并通过“取消注释此行”来查看代码是否有效。
    【解决方案6】:

    只要放 scanf("%d\n",&amp;loops);

    而不是 scanf("%d",&amp;loops);

    【讨论】:

    • 虽然这个答案涵盖了原始问题的一个重要现象,但它未能提供对问题本身的一些洞察。因此,从长远来看,它充其量对预期的原始问题有帮助,但并没有真正的帮助。请添加更多解释,以使您的回答对以后寻找类似问题的读者有所帮助。
    【解决方案7】:

    在变量标签循环中扫描整数后忽略以下换行符(由于按 ENTER)的另一种方法是:

    scanf ("%d%*c", &loops);
    

    在哪里,根据手册页:

    * 禁止赋值。接下来的转换照常进行,但不使用指针;转换的结果被简单地丢弃。

    这不太可能,但是在 scanf 期间检查错误的好习惯:

    errno = 0
    scanf ("%d%*c", &loops);
    if (errno != 0) perror ("scanf");
    // act accordingly to avoid un-necessary bug in the code
    

    例如,在您的代码中,loops 是一个本地未初始化的变量,包含垃圾。如果 scanf 未能填充所需的值,则后面的 while 循环可能会任意运行。

    【讨论】:

    • 这不是您从 scanf 检查错误的方式
    【解决方案8】:

    scanf() 将该行末尾的以下 \n 留在缓冲区中,fgets() 将读取它。为了解决这个问题,我们必须做一些事情来消耗换行符。让我们看看问题。

    问题

    #include<stdio.h>
    void main()
    {
        char input[10];
        printf("Enter in fgets: ");
        scanf("%s", input);
        getchar();
        printf("Enter in scanf: ");
        fgets(input, 10, stdin);
    }
    
    输出:
    Enter in scanf: Hello
    Enter in fgets:
    

    如您所见,它没有显示 fgets 部分。让我们看看解决方案。

    把getchar()放在scanf和fgets()之间

    #include<stdio.h>
    void main()
    {
        char input[10];
        printf("Enter in fgets: ");
        scanf("%s", input);
        getchar();
        printf("Enter in scanf: ");
        fgets(input, 10, stdin);
    }
    
    输出:
    Enter in scanf: Hello
    Enter in fgets: Hello
    

    如果可能的话,在fgets() 之后使用scanf()

    #include<stdio.h>
    void main()
    {
        char input[10];
        printf("Enter in fgets: ");
        fgets(input, 10, stdin);
        getchar();
        printf("Enter in scanf: ");
        scanf("%s", input);
    }
    
    输出:
    Enter in fgets: Hello
    Enter in scanf: Hello
    

    把fget()放2次(这个方法有些时候不行)

    #include<stdio.h>
    void main()
    {
        char input[10];
        printf("Enter in fgets: ");
        scanf("%s", input);
        getchar();
        printf("Enter in scanf: ");
        fgets(input, 10, stdin);
        fgets(input, 10, stdin)
    }
    
    输出:
    Enter in scanf: Hello
    Enter in fgets: Hello
    

    将sscanf()插入scanf()

    #include<stdio.h>
    void main()
    {
        char input[10];
        printf("Enter in fgets: ");
        sscanf(hello, "%s", input);
        getchar();
        printf("Enter in scanf: ");
        fgets(input, 10, stdin);
    }
    
    输出:
    Enter in sscanf: Hello
    Enter in fgets: Hello
    

    【讨论】:

    • 您介意进一步详细说明吗?例如。举个例子。
    猜你喜欢
    相关资源
    最近更新 更多