【问题标题】:About the mechanism of using fgets() and stdin together关于 fgets() 和 stdin 一起使用的机制
【发布时间】:2018-06-14 09:30:11
【问题描述】:

我想更好地了解如何使用fgets()stdin。 以下是我的代码:

int main()
{
    char inputBuff[6];
    while(fgets(inputBuff, 6, stdin))
    {
        printf("%s", inputBuff);
    }
    return 0;
}

假设我的输入是 aaaabbbb,然后按 Enter。通过使用循环计数,我知道实际上循环将在下一次输入之前运行两次(包括我输入的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() 有什么误解吗?感谢您的宝贵时间!

【问题讨论】:

  • 目前还不清楚现实在什么时候与您的预期不同。你能显示产生意外输出的输入吗?
  • 来自精品手册:fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A '\0' is stored after the last character in the buffer.
  • 您是否期望 "bbb\n" 被丢弃,并要求新的输入?如果是这样,程序如何在事先不知道长度的情况下读取一行?
  • 你到底是什么意思 "我只是想知道为什么我不能使用键盘在循环 2 中输入任何内容,而 fgets() 只是继续从标准输入流中检索 5 个字符...... "?您是想在前一个字符串的中间插入一些东西(bbb\n 之前),还是让fgets 读到换行符之后,还是什么?
  • 对不起,我有一些编码任务要做,所以我无法立即回复您的 cmets。抱歉这么晚才回复。 @n.m。不,我没有收到任何意外的输出。只是想知道为什么“有时”在调用 fgets() 时不要求我输入某事。

标签: c stdin fgets


【解决方案1】:

您的程序的行为比您预期的要微妙:

fgets(inputBuff, 6, stdin) 最多从stdin 读取 5 个字节,当它得到一个换行符时停止读取,该换行符存储到目标数组中。

因此,当您正确诊断时,第一个调用读取 5 个字节 aaab 并打印它们,第二个调用读取 4 个字节 bbb\n 并打印它们,然后第三个调用获取一个空输入流并等待用户输入.

棘手的部分是stdin 如何从用户那里获取输入,也称为控制台输入。

默认情况下,控制台输入和stdin 通常都是行缓冲的,因此无论传递给fgets() 的缓冲区大小如何,您都可以键入完整的输入行。但是,如果您可以将stdin 设置为无缓冲并将控制台输入设置为未煮熟,那么第一个fgets() 确实会在您键入它们后立即读取前5 个字节。

控制台输入是一个复杂的主题。这是一篇关于其内部运作的深入文章:https://www.linusakesson.net/programming/tty/

【讨论】:

  • 非常有用且详细的信息!谢谢你的回答!
【解决方案2】:

无论您问什么,fgets() 的手册页中都有所有内容。只需要正确阅读它,它说

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

fgets() 最多比 sizecharacters 读取 一个 从流并将它们存储到 s 指向的缓冲区中。读 在 EOF 或换行符之后停止。 如果读取换行符,则为 存入缓冲区。存储终止空字节 (aq\0aq) 在缓冲区中的最后一个字符之后。

如果输入是aaaabbbb 并且在fgets() 第二个参数中,您将大小指定为6,即它将少读一个5 字符并添加终止\0,所以第一次inputBuff 保持@987654329 @ 并且由于仍然 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() 从流中读取最多比 sizecharacters 少一个,并将它们存储到 s 指向的缓冲区中。在 EOF 或换行符后停止读取。”这是否意味着实际上 fgets() 在将“aaaab”存储在缓冲区中之后还没有完成其“第一次”读取输入过程?就在读取“bbb\n”之后,fgets() 开始第二次请求用户输入。如果是这样,为什么即使 fgets() 没有停止读取(到达 EOF 或换行符),下面的 printf() 仍然可以执行?
  • @GarethLam fgets 没有读完整个行,但它确实读了部分行并且 到此为止。你可以printf到目前为止阅读的部分。下一次,它将从中断的地方开始读取。如果该行超过 5 个字符,那么您的程序不能处于读取整行并准备好打印的状态,因为根本没有空间容纳整行。它可以做的最好的事情,而且确实做到了,就是逐段阅读较长的行。
  • @n.m.看来我对 fgets() 和 stdin 没有很好的理解。不知道我需要在 fgets() 读取整行后等待输入新字符。谢谢你们的cmets。我对这2个功能确实有更深入的了解。
【解决方案3】:

fgets() 只读取到'\n'EOF。之后的所有内容都将留在stdin 中,因此当您再次调用fgets() 时会被读取。但是,您可以从stdin 中删除多余的字符,例如使用getc(),直到到达'\0'。您可能需要查看手册页。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-06
    • 2017-10-28
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 2019-03-18
    • 2021-05-16
    相关资源
    最近更新 更多