ftell(FILE * fd)和lseek(int fd,off_t offset,int whence)的结果之间的差异
考虑这个代码示例:
#include #include #include int main() { //this file exists and contains data: "ABCDEFGHIJKLM" FILE* file = fopen("file.txt", "r"); char data[4]; long int pos = ftell(file); fseek(file, 0, SEEK_SET); fread(data, 4, 1, file); fseek(file, pos, SEEK_SET); printf("ftell: %d\n", ftell(file)); printf("lseek: %d\n", lseek(fileno(file), 0, SEEK_CUR)); fread(data, 1, 4, file); //this correctly prints A //but external function needs fileno(file) that has wrong pos printf("%c\n", data[0]); fclose(file); return 0; }
该计划的结果令人惊讶:
ftell: 0 lseek: 14 A
我正在尝试修复我的应用程序中的错误,但我确定前一段时间此程序的结果应为0, 0
(换句话说,从来没有在我的应用程序中出现此错误)。
libc改变了这种奇怪的情况发生了什么?
我怎么能以良好的方式解决这个问题?
lseek(fileno(file), 0, SEEK_SET)
是一个很好的解决方案吗?
使用标准库文件操作(例如fread(3)
, fseek(3)
)以及低级系统调用(例如read(2)
, lseek(3)
)是危险的。
这是有问题的原因是因为标准库将缓冲事物,而不是立即将它们写入(到文件描述符)(取决于缓冲模式)。
如果需要访问基础文件描述符,则应确保在获取其fileno
之前fflush
流。 我在头文件中抛出了这样的东西:
/** * Safely get the file descriptor associated with FILE, * by fflush()ing its contents first. */ static inline int safe_fileno(FILE *f) { fflush(f); return fileno(f); }
此外,一旦你调用它,你可能不应该再回到使用FILE*
了,因为你已经改变了内核文件指针,但是标准库可能会认为它没有改变(因为你最后使用它)。 正如评论中提到的 ,您可以将FILE*
与文件描述符重新同步,如下所示:
/** * Re-synchronize the file offset of a FILE with the * file offset of its underlying file descriptor. */ static inline void fresync(FILE *f) { off_t off = lseek(fileno(f), 0, SEEK_CUR); fseek(f, off, SEEK_SET); }