【问题标题】:How to determine number of characters that were read with fgets()?如何确定使用 fgets() 读取的字符数?
【发布时间】:2018-05-28 18:42:19
【问题描述】:

这是手册页中fgets() 的描述:

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

RETURN VALUE
  fgets() returns s on success, and NULL on error or  when  end  of  file
  occurs while no characters have been read.

它不遵循read 的模式,它在失败时返回-1,在成功时返回读取的字节数。相反,它返回一个char*,失败时为NULL,成功时为s。这并没有给我任何关于输入多长时间的信息。所以如果我有这样的事情:

char input_buffer[256];
fgets(input_buffer, sizeof(input_buffer), stdin);

fgets 调用之后,有没有办法知道输入在不先对缓冲区进行零初始化的情况下持续了多长时间?

谢谢。

【问题讨论】:

  • Return value of fgets()的可能重复
  • int len= strlen(input_buffer); 会给你。
  • 这就是为什么我不喜欢fgets() 的设计,我更喜欢fread() 但它不处理语言环境。
  • 很高兴知道strlen 可以解决问题,但是有没有不涉及 O(n) 迭代的解决方案?当fgets 已经知道长度是多少时,您必须再次弄清楚这似乎很浪费。
  • @Stargateur, fread() 不会 NUL 终止输入,但 fgets() 会。所以strlen() 适用于fgets(),但不适用于fread()。每个功能都有其目的和功能。函数fgets() 是这样编写的,因此可以“链接”函数:fread() 不是。函数:fgets() 用于文本输入。函数:fread() 用于二进制输入。

标签: c io fgets stdio


【解决方案1】:

如何确定使用 fgets() 读取的字符数?

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

检查fgets()返回值后使用strlen(s)

if (fgets(s, size, stream)) {
  printf("number of characters that were read: %zu\n", strlen(s));
} else if (feof(stream)) {
  printf("number of characters that were read:0 End-of-file\n");
} else  {
  printf("number of characters that were read unknown due to input error\n");
}

这有效除非一个空字符'\0' 被读取为strlen() 将遇到'\0' 在函数附加一个之前。在这种情况下,fgets() 之后的strlen(s) 将报告一个较小的值。

预填充s 然后调用fgets() 有多种技巧,但未定义的未读缓冲区的其余部分会发生什么。还存在其他缺点。

如果空字符作为有效输入流的一部分是一个问题,请使用fgetc()getline()之类的东西。


空字符 是文本的常见情况是文本编码为 UTF-16。当然,代码不应该使用fgets() 来阅读该文本,但这需要先验知识。由于错误地假设文本文件是非空字符文本文件,许多读取 text 的代码都以神秘的方式失败了。

此外,即使文本文件据称缺少 空字符,以下代码会发生什么情况?

if (fgets(s, size, stream)) {
  size_t len = strlen(s);
  s[--len] = '\0';  // poor way to lop off the trailing \n, this could be UB
}

这样的代码调用未定义的行为,并带有黑客攻击:在开头的文件中插入一个空字符。 (请参阅 thisthis 以获取更好的解决方案来消除潜在的 \n

健壮的代码不假设文本格式正确,并采取措施检测异常。


迂腐提示:fgets(char *s, int size, FILE *stream);size < 2 存在病理问题。

【讨论】:

  • 对一些通常在大多数人的雷达下飞行的重要观点表示敬意——包括我......
【解决方案2】:

是的,有。如果成功,它总是以空值终止。所以它将是strlen(buf)

从标准 7.21.7.2

char *fgets(char * restrict s, int n,FILE * restrict stream); fgets 函数最多读取比 由n 指定的字符从stream 指向的流到 s 指向的数组。之后没有额外的字符被读取 换行符(保留)或文件结尾之后。 一个空值 在读入最后一个字符后立即写入字符 数组。

【讨论】:

  • 当然,前提是输入本身不包含空字符,这会破坏这个方案。但这是一个常见的限制。
  • @JohnBollinger.: 是的,情况确实如此,但为了避免这种情况,我们还可以进行另一项检查(这同样不完全可靠),即检查\n
  • 是的,如果提供给fgets() 的缓冲区足够大,可以容纳包括换行在内的整行,检查换行就可以了。在许多情况下,假设或要求输入不包含空值比假设或要求它不包含长行更安全。这里需要注意的是,fgets() 最适合 text,因此输入不得包含空值的条件通常是一个很低的标准。
  • @JohnBollinger 如果您的文本预计包含空终止符,那么fgets 可能不应该是您的首选阅读功能。
  • @JohnBollinger.: 所以假设,如果输入包含空值并且行不够大,无法容纳\n,那么我想这些都行不通。 (这不太可能)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-17
  • 2022-11-17
  • 2018-10-14
相关资源
最近更新 更多