使用fgets()和stdin的机制是什么?

4
我希望能更好地理解如何使用 fgets()stdin。以下是我的代码:
int main()
{
    char inputBuff[6];
    while(fgets(inputBuff, 6, stdin))
    {
        printf("%s", inputBuff);
    }
    return 0;
}

假设我的输入是aaaabbbb,我按下回车键。通过使用循环计数器,我知道在下一次输入之前实际上循环将运行两次(包括我输入的aaaabbbb)。
第1次循环:在我输入字符后,aaaabbbb\n将被存储在stdin文件流的缓冲区中。然后,fgets()将从文件流中检索特定数量的数据并将它们放入inputBuff中。在这种情况下,它将每次检索5个(6-1)个字符。因此,当fgets()已经运行一次时,inputBuff将存储aaaab,然后被打印出来。
第2次循环:由于bbb\n仍然留在文件流中,fgets()将执行第二次,以便inputBuff包含bbb\n,然后被打印出来。
第3次循环:程序将要求我输入(第二次),因为文件流已到达结尾(EOF)。
问题:似乎fgets()只会在stdin流中没有剩余数据(EOF)时才要求我输入。我只是想知道为什么我不能在第2次循环中使用键盘输入任何内容,fgets()继续从stdin流中检索5个字符,并将多余的数据留在文件流中以供下一次检索。我是否对stdinfgets()有任何误解?谢谢你的时间!

现实与您的期望分歧的确切时间点并不清楚。您能否展示产生意外输出的输入? - n. m.
从Fine手册中:fgets()函数从流中最多读取size-1个字符,并将它们存储到指向s的缓冲区中。当遇到EOF或换行符时,读取停止。如果读取到换行符,则将其存储到缓冲区中。在缓冲区中的最后一个字符后面存储'\0'。 - joop
你是否期望"bbb\n"被丢弃,然后请求新的输入?如果是这样,那么一个程序如何在未知长度的情况下读取一行呢? - Weather Vane
@ilkkachu,对不起我的英语不太好。我想知道为什么有时候调用fgets()时没有要求我输入任何内容。在这种情况下,只有当标准输入流达到结尾时,我才会被要求使用键盘输入东西。 - Gareth Lam
函数 fgets 会一直读取到行末或者缓冲区已满。第一次调用停止是因为缓冲区已满。第二次调用停止是因为到达了行末。如果 fgets 等待更多的输入,它可以读取 两行,但它并没有这样做。它只读取一行。它不需要更多的输入,因为它已经有足够的输入了。 - Weather Vane
显示剩余5条评论
3个回答

1

char *fgets(char *s, int size, FILE *stream);

fgets()函数从流中读取不超过size-1个字符,并将它们存储到指向s的缓冲区中。当读取到EOF或换行符时,读取停止。如果读取到一个换行符,则将其存储到缓冲区中。在缓冲区中最后一个字符之后存储一个终止空字节(aq\0aq)。

如果输入为aaaabbbb,并且在fgets()的第二个参数中指定了大小为6,即它将读取一个少于5个字符,并添加终止符\0,因此第一次inputBuff保持aaaab,由于仍然没有出现EOF\n,所以下一次inputBuff保持bbb\n,因为新行也被存储在最后。

此外,您应该检查fgets()的返回类型,并检查是否出现\n,然后中断循环。例如:
char *ptr = NULL;

while( (ptr = fgets(inputBuff, 6, stdin))!= NULL){
          if(*ptr == '\n')
                   break;
          printf("%s", inputBuff);
}

谢谢提醒我需要添加一个条件来打破循环。"fgets()从流中读取最多比size少一个字符,并将它们存储到指向s的缓冲区中。在EOF或换行符后停止读取。"这是否意味着实际上fgets()在将"aaaab"存储在缓冲区后还没有完成其“第一次”读取输入过程?就在读取"bbb\n"之后,fgets()开始了对用户输入的第二个请求。如果是这样,为什么下面的printf()语句仍然可以执行,即使fgets()尚未停止读取(达到EOF或换行符)? - Gareth Lam
@GarethLam fgets 没有读取完整行,但它确实读取了部分行并在此处停止。您可以使用 printf 打印到目前为止读取的部分。下一次,它将从离开的地方开始阅读。如果该行超过5个字符,则您的程序不能处于已读取并准备好打印整个行的状态,因为没有足够的空间来容纳整个行。它能做到的最好的事情,也确实做到了,就是逐段阅读更长的行。 - n. m.
@n.m. 看来我对fgets()和stdin没有很好的理解。我不知道在fgets()读取整行后需要等待输入新字符。谢谢你的评论。我现在对这两个函数有更深入的理解了。 - Gareth Lam

1

fgets() 只读取到 '\n' 或者 EOF,之后的内容将留在 stdin 中,在下一次调用 fgets() 时会被读取。你可以通过使用例如 getc() 直到达到 '\0' 的方式从 stdin 中移除多余的字符。你可能需要查看相关文档。


1
您的程序行为比您期望的要微妙一些: fgets(inputBuff, 6, stdin) 最多从 stdin 读取 5 个字节,并在获取换行符时停止读取,然后将其存储到目标数组中。
因此,正如您正确诊断的那样,第一次调用读取了 5 个字节的 aaab 并将其打印出来,第二次调用读取了 4 个字节的 bbb\n 并将其打印出来,然后第三次调用获取了一个空输入流并等待用户输入。
棘手的部分是 stdin 如何从用户获取输入,也称为控制台输入。
默认情况下,控制台输入和 stdin 通常都是行缓冲的,因此您可以输入完整的一行输入,而不管传递给 fgets() 的缓冲区大小。但是,如果您将 stdin 设置为无缓冲,并将控制台输入设置为未加工,则第一个 fgets() 将确实在您键入前五个字节时立即读取它们。

控制台输入是一个复杂的主题。这是一篇深入的文章,介绍了其内部工作原理:https://www.linusakesson.net/programming/tty/


非常有用且详细的信息!感谢您的回答! - Gareth Lam

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