为什么 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()
.
恢复它
一个在我的 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()
.