为什么 fgetc 将文件位置指示器向后移动?

Why does fgetc move the file position indicator backwards?

一个在我的 freeBSD 系统上运行良好的程序在 windows (Visual Studio 15) 上构建时失败了。在这里进入死循环:

//...
while (1) {
    if ('@' == fgetc(f)) {
        // we do some stuff here. irrelevant for Whosebug question
        break;
    }        
    fseek(f, -1, SEEK_CUR);        
    if (0 != fseek(f, -1, SEEK_CUR)) {
        // Beginning of file.
        break;
    }
}
//...

仔细观察(通过添加一堆 fgetpos() 调用)我发现 fgetc 将文件位置指示器向后移动。所以它会错过文件的开头和一些'@',如果它们不在末尾的 3 的倍数位置。

我注意到只有在使用

打开文件 f 时才会发生这种情况
fopen(filename, "a+");
//text mode read/append

当我把它改成

fopen(filename, "ab+");
//binary mode read/append

然后一切都按预期工作。 我认为对于我的代码来说,一直使用二进制模式是安全的。 但还有两个问题:

引用 C11 7.21.9.2 fseek 函数:

For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET.

在以文本模式打开的流上使用 SEEK_CUR 的 whence 参数调用 fseek 未包含在 C 标准中。以二进制模式打开文件似乎是一个更好的选择。

fgetpos() 返回的值作为文件中的偏移量可能没有意义,它只是作为参数传递给 fsetpos()

一般来说,您应该尝试更改算法以避免依赖流中的向后查找,尤其是依赖 fseek() 错误似乎不可靠。而是使用 ftell()fgetpos() 保存 fgetc() 之前的位置,并在需要时使用 fseek(pos, SEEK_SET, fp)fsetpos().

恢复它