如何确定使用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'
,否则此方法有效,因为函数会将后续的字符追加到字符串最后。但如果读取到了'\0'
,那么strlen()
在函数之前就会遇到空字符'\0'
,导致其返回较小的值。
有各种技巧可以预先填充s
,然后调用fgets()
,但未读取缓冲区中剩余内容时会出现不确定情况,还存在其他缺陷。
如果担心输入流中包含空字符,则应使用fgetc()
或类似的getline()
方法。
当文本以UTF-16编码时,空字符往往是文本中的一部分。当然,代码不应该使用fgets()
来读取这些文本,但这需要提前知道。由于错误地假定文本文件是非空字符的文本文件,许多读取文本的代码已以神秘方式失败。
此外,即使文本文件不包含空字符,下面的代码会发生什么呢?
if (fgets(s, size, stream)) {
size_t len = strlen(s);
s[--len] = '\0';
}
这种代码在文件开头的一行中插入一个null字符,会触发未定义行为,从而被黑客利用。 (有关更好的解决办法,请参见此处和此处以剪掉潜在的\n
)
健壮的代码不假设文本格式良好,并采取措施检测异常。
严谨的注释:使用fgets(char *s, int size, FILE *stream);
时,如果size < 2
,可能会出现病态问题。
int len= strlen(input_buffer);
这个语句将会给你输入缓冲区的长度。 - Paul Ogilviefgets()
的设计,我更喜欢fread()
,但它不能处理本地化。 - Stargateurstrlen
可以解决问题,但是有没有不涉及 O(n) 迭代的解决方案呢?似乎很浪费时间,因为fgets
已经知道长度了。 - James Kofread()
不会在输入末尾添加NUL,但是fgets()
会。因此,strlen()
适用于fgets()
但不适用于fread()
。每个函数都有其目的和能力。函数fgets()
被编写为可以“链接”。而函数fread()
则不行。函数fgets()
用于文本输入。函数fread()
用于二进制输入。 - user3629249fgets()
函数中int
类型的大小参数,也不喜欢需要使用strlen()
来知道字符串的大小。getline()
函数更好,但只适用于posix系统,fread()
函数不会添加空字符,但你可以很容易地添加。就像我所说的,fread()
与fgets()
在本地化、空字符等方面并不相同,但它具有更好的设计。 - Stargateur