POSIX为何允许只读模式下fseek越过文件末尾?其实用价值何在?
关于POSIX中只读模式下fseek越过文件末尾的疑问解答
这两个问题问到点子上了,很多刚接触POSIX文件API的开发者都会有类似困惑,我来结合标准和实际场景给你解释清楚:
1. 为什么POSIX允许只读模式下调用fseek越过文件现有末尾?
核心原因在于:fseek只是修改进程内的文件偏移量指针,并不会对实际文件内容做任何修改。POSIX对只读模式("rb"这类)的限制,是禁止对文件执行写入、截断、扩展等会修改文件的操作,而定位操作本身属于“查询/设置状态”的范畴,并不触及文件的实际存储内容。
根据POSIX标准对fseek的定义,只有当出现以下情况时才会返回错误:
- 传入的偏移量是无效值(比如负数且
SEEK_SET) - 底层存储设备不支持该定位操作(比如管道、字符设备)
- 进程没有足够的权限进行定位(不过只读模式下通常都有定位权限)
而偏移量超过文件当前长度,并不属于错误场景——因为这个操作只是告诉操作系统“接下来的读写操作从这个位置开始”,至于后续读取会不会拿到数据,那是fread的事情。就像你在一本书里夹了个书签在第100页,但这本书只有10页,书签本身是允许存在的,只是翻到那一页时没有内容而已。
看你的测试代码,fseek(f, 100500, SEEK_SET)返回0是完全符合标准的,因为这个操作没有修改文件,只是设置了偏移量,只读模式的限制不影响这个动作。
2. 越过文件末尾的定位操作有什么实用价值?
这种操作看起来有点反直觉,但在实际开发中确实有不少场景会用到:
- 验证固定格式文件的完整性:比如某些二进制协议的文件,头部会定义整个文件的预期大小。你可以只读打开文件后,直接定位到预期的文件末尾偏移处,如果
fseek成功但后续读取不到数据,就可以判断文件是不完整的,提前抛出错误。 - 统一处理文件读取逻辑:假设你的程序需要处理两种情况:文件已经写完,或者文件还在被其他进程写入。只读打开后,直接定位到你需要读取的位置,不管当前文件长度够不够,
fread都会返回实际读到的字节数(不够的话返回0),这样你可以用同一套逻辑处理完整和不完整的文件。 - 模拟未来的文件状态:比如你在开发一个文件处理工具,需要测试当文件被扩展到很大时的读取行为,不需要实际创建大文件,只需要用
fseek跳到对应的偏移,然后测试读取逻辑的边界处理即可。
再看你的测试代码,后续调用fread会返回0,因为从偏移100500处开始,文件没有任何数据可读,这是符合预期的行为——fseek允许你跳到那里,但读取操作会如实返回没有数据的结果。
附格式化后的测试代码:
#include <cassert> #include <cstdio> #include <cstring> #include <errno.h> // 补充errno头文件,否则无法正确获取错误信息 int main() { std::FILE* f = std::fopen("tmp_file.txt", "wb"); auto result = std::fwrite("1", 1, 1, f); assert(result == 1); result = std::fclose(f); assert(result == 0); f = std::fopen("tmp_file.txt", "rb"); // READ ONLY binary mode result = std::fseek(f, 100500, SEEK_SET); assert(result == 0); // 这里成功是因为fseek不修改文件,只读模式不限制定位 char buff[100500] = {0}; result = std::fread(&buff, sizeof(buff), 1, f); printf("result = %zu, errno: %s\n", result, strerror(errno)); // 这里result会是0,errno大概率是0(因为没有错误,只是没读到数据) std::fclose(f); return 0; }
内容的提问来源于stack exchange,提问作者leanid.chaika
相关产品推荐
相关产品推荐

