fgets()函数是否总是以\0终止字符缓冲区?

22

fgets()是否总是使用\0终止字符缓冲区,即使已经到达EOF?看起来它确实这样做(在ANSI K&R书中给出的实现中肯定如此),但我想询问以确保。

我猜这个问题也适用于其他类似的函数,比如gets()。

编辑:我知道在“正常”情况下会附加\0,我的问题针对的是EOF或错误条件。例如:

FILE *fp;
char b[128];
/* ... */
if (feof(fp)) {
    /* is \0 appended after EACH of these calls? */
    fgets(b, 128, fp);
    fgets(b, 128, fp);
    fgets(b, 128, fp);
}

1
始终检查 fgets() 的返回值:if (fgets(...) == NULL) /* 不确定的数组 */; - pmg
你可能是对的,但我看到其他答案指出,即使fgets()返回NULL\0也总是被附加的。 - Ree
5个回答

17

fgets 会将一个 '\0' 字符添加到读取的缓冲区中,它最多从流中读取 size - 1 个字符(其中 size 是第二个参数),因此请注意。

永远不要使用 gets 函数,因为您无法保证它不会溢出任何给定的缓冲区,尽管它在技术上总是终止读取的字符串,但实际上这并没有帮助。


8

永远不要使用gets!

    7.19.7.2 fgets函数
    概要
1           #include <stdio.h>
            char *fgets(char * restrict s, int n,
                 FILE * restrict stream);
    描述
2   fgets函数从stream指向的流中最多读取n-1个字符到s指向的数组中。在新行字符(保留该字符)或文件结尾之后不会再读取其他字符。一个空字符将立即写入数组中最后读取的字符之后。
    返回值
3   如果成功,fgets函数返回s。如果遇到文件结尾并且没有将任何字符读入数组,则数组内容保持不变,并返回空指针。如果在操作过程中发生读取错误,则数组内容是不确定的,并返回空指针。

因此,是的,当fgets()未返回NULL时,目标数组始终具有空字符。

如果fgets()返回NULL,则目标数组可能已更改并且可能没有空字符。从fgets()获取NULL后,永远不要依赖于数组。


编辑添加示例

$ cat fgets_error.c
#include <stdio.h>
void print_buf(char *buf, size_t len) { int k; printf("%02X", buf[0]); for (k=1; k<len; k++) printf(" %02X", buf[k]); }
int main(void) { char buf[3] = {1, 1, 1}; char *r;
printf("输入CTRL+D:"); fflush(stdout); r = fgets(buf, sizeof buf, stdin); printf("\nfgets返回%p,buf具有[", (void*)r); print_buf(buf, sizeof buf); printf("]\n");
return 0; } $ ./a.out 输入CTRL+D: fgets返回(nil),buf具有[01 01 01] $

看到了吗?buf中没有空字符 :)


@pmg说“永远不要使用gets()函数!!”,你有文件读取的替代方案吗? - atlex2
1
@atlex2:是的,另一种选择是fgets() - pmg
如果你可以接受限制在最近的类POSIX系统(如Linux和OSX)中或者自己编写缺失平台上的版本,那么getline是更好的选择。 - hyde

4

man fgets:

fgets()函数从流中读取最多比size少一个字符的内容,并将其存储到指向s的缓冲区中。当遇到EOF或换行符时,读取停止。如果读取到换行符,则将其存储到缓冲区中。在缓冲区的最后一个字符后面存储'\0'。


2

如果你是以二进制模式 "rb" 打开文件,并且想要使用 fgets 按行读取文本,你可以使用以下代码来保护你的软件免受文本丢失的影响,即使文本中包含了 '\0' 字节也不会出错。

但是像其他人提到的那样,通常情况下,如果流中包含 '\0',就不应该使用 fgets


size_t filepos=ftell(stream);
fgets(buffer, buffersize, stream);
len=strlen(buffer);
/* now check for > len+1 since no problem if the 
   last byte is 0 */
if(ftell(stream)-filepos > len+1) 
{
    if(!len) filepos++;
    if(!fseek(stream, filepos, SEEK_SET) && len)
    {
        fread(buffer, 1, len, stream);
        buffer[len]='\0';
    }
}

1

是的,它可以。来自CPlusPlus.com

从流中读取字符并将它们存储为 C 字符串到 str 中,直到读取了 (num-1) 个字符或者遇到换行符或文件结束标志 EOF,以先到者为准。

换行符会使 fgets 停止读取,但被视为有效字符,并因此包含在复制到 str 中的字符串中。

在读取的字符后,会自动在 str 中添加一个空字符来表示 C 字符串的末尾。


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