为什么gets()函数能够正常工作,而fgets()函数不能?

3
我写了一个程序,发现了 gets() 函数并使用它,效果很好。但是当我编译程序时,它提示 gets() 函数存在安全隐患。因此,我开始寻找替代方案,尽管 gets() 函数能够产生正确的输出,但出于安全考虑,我更愿意使用另一种不会带来风险的替代方案,这就是我找到 fgets() 的原因。我认为代码将会和之前一样编译,并得到相同的输出结果。然而,我的 if 语句却没有被输出。

以下是代码:

#include <stdio.h>
#include <string.h>

int main()
{
        char qOne[10];
        char qTwo[10];
        char guess[40] = "My guess is that you are thinking of a\0";

        printf("TWO QUESTIONS\n");
        printf("Think of an object, and i'll try to guess it.\n\n");

        printf("Question 1) Is it an animal, vegetable, or mineral?\n");
        printf("\n> ");
        //gets(qOne);
        fgets(qOne, 10, stdin);

        printf("\nQuestion 2) Is it bigger than a breadbox? (yes/no)\n");
        printf("\n> ");
        //gets(qTwo);
        fgets(qTwo, 10, stdin);

        printf("\n");

        if(strcmp(qOne, "animal") == 0 && strcmp(qTwo, "no") == 0 ||strcmp(qOne, "animal") == 0 && strcmp(qTwo, "No") == 0)

                printf("%s squirrel.\n", guess);

        else if(strcmp(qOne, "animal") == 0 && strcmp(qTwo, "yes") == 0 ||strcmp(qOne, "animal") == 0 && strcmp(qTwo, "Yes") == 0)

                printf("%s moose.\n", guess);

        else if(strcmp(qOne, "vegetable") == 0 && strcmp(qTwo, "no") == 0 ||strcmp(qOne, "vegetable") == 0 && strcmp(qTwo, "No") == 0)

                printf("%s carrot.\n", guess);

        else if(strcmp(qOne, "vegetable") == 0 && strcmp(qTwo, "yes") == 0 ||strcmp(qOne, "vegetable") == 0 && strcmp(qTwo, "Yes") == 0)

                printf("%s watermelon.\n", guess);

        else if(strcmp(qOne, "mineral") == 0 && strcmp(qTwo, "no") == 0 ||strcmp(qOne, "mineral") == 0 && strcmp(qTwo, "No") == 0)

                printf("%s paper clip.\n", guess);

        else if(strcmp(qOne, "mineral") == 0 && strcmp(qTwo, "yes") == 0 ||strcmp(qOne, "mineral") == 0 && strcmp(qTwo, "Yes") == 0)

                printf("%s Camaro.\n", guess);


        printf("\nI would ask you if I'm right, but I don't actually care.\n");

        return 0;
}

这段代码的输出结果是(输入字符串“animal”和“yes”):
TWO QUESTIONS
Think of an object, and i'll try to guess it.

Question 1) Is it an animal, vegetable, or mineral?

> animal

Question 2) Is it bigger than a breadbox? (yes/no)

> yes


I would ask you if I'm right, but I don't actually care.

然而,当我只使用gets()而不是fgets()时,我的代码会输出正确的结果,即:
TWO QUESTIONS
Think of an object, and i'll try to guess it.

Question 1) Is it an animal, vegetable, or mineral?

> animal

Question 2) Is it bigger than a breadbox? (yes/no)

> yes

My guess is that you are thinking of a moose.

I would ask you if I'm right, but I don't actually care.

我该如何使用fgets()来获得相同的输出结果?

不是“可能”,而是确实如此。gets()函数会用\0替换输入中的换行符,但fgets()函数会保留它。 - chepner
在成熟的 C 库实现中,fgets()gets() 可能都正常工作。 - The Paramagnetic Croissant
2
如果fgets()在目标数组中没有留下尾随的'\n',那么这意味着输入行比缓冲区长 - 这意味着输入行的其余部分仍然留在原地等待下一个输入调用读取。你应该做一些处理这种情况的事情。(例如,你可以只读取和丢弃字符,直到看到'\n'EOF)。 - Keith Thompson
1
只是提醒一下,gets会在您的代码中引入故障点,并且自2011年修订以来不再是标准库的一部分。您真的不想在任何情况下使用它。它非常不安全。 - John Bode
1
Keith Thompson是正确的,“如果fgets()没有留下尾随的'\n'...输入行比缓冲区长”肯定是最有可能的原因。其他可能性包括:1)文件结束并且最后一个字符不是'\n'。2)在'\n'之前出现了嵌入的'\0'掩盖了它的存在。3)未将fgets()返回值与NULL进行检查,缓冲区内容可能是不确定的。 - chux - Reinstate Monica
1个回答

6

fgets() 保留了末尾的换行符 '\n',而 gets() 则没有。因此,比较操作失败。@user3121023 添加代码以消除潜在的末尾换行符。

if (fgets(qTwo, sizeof qTwo, stdin) == NULL) Handle_EOF();
qTwo[strcspn(qTwo, "\n")] = 0;

从fgets()输入中删除尾随换行符


[编辑]

请注意@Keith Thompson上面关于输入行过长的评论。


2
注意,fgets() 保存 '\n' 需要比 gets() 多一个字符的缓冲区。顺便说一句,最好不要使用 gets() - chux - Reinstate Monica
当我添加那段代码时,我不知道Handle_EOF()是什么,我把代码放在fgets之后,但我仍然有点迷惑。 - Derrick Rose
1
@Asad Mahmood Handle_EOF() 意味着你需要编写一些代码来处理这种情况。fgets() 返回 qTwoNULL。当它返回 NULL 时,意味着文件结束(stdin 已关闭),或者很少出现输入错误。对于像这样简单的程序,建议使用 if (fgets(qTwo, sizeof qTwo, stdin) == NULL) { puts("EOF Occurred"); return -1; } - chux - Reinstate Monica
非常好的工作,加一分给你 :) - Derrick Rose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接