seekg(0, ios::end) 并非适用于所有文件类型,仅对可定位流(如普通磁盘文件)有效;对管道、终端、NFSv3、/proc伪文件等会失败并置failbit,需检查fail()并回退至stat()等系统调用。

seekg(0, ios::end) 为什么不能直接用在所有文件上
它确实能跳到末尾再用 tellg() 拿长度,但前提是流处于“可定位”状态——比如普通磁盘文件可以,而管道、终端输入、某些网络挂载的文件系统(如 NFSv3 默认)或 /proc 下的伪文件就不行。一旦失败,tellg() 返回 -1,且流会进入 failbit 状态,后续读取直接失效。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 调用
seekg(0, ios::end)后必须立刻检查!file_stream.fail(),不能只看返回值 - 若失败,改用
stat()系统调用更可靠:struct stat sb; if (stat("path", &sb) == 0) size = sb.st_size; - Windows 下注意:用
_fstat()或GetFileSizeEx(),避免跨平台时stat的符号扩展问题
ifstream 打开大文件前必须关掉 skipws 和 sync_with_stdio
默认构造的 ifstream 会同步 C 标准库 I/O,且可能开启空格跳过(skipws),这在 seek 前不显式关闭,会导致 seekg 行为异常(尤其文本模式下换行符处理干扰位置计算)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 打开时强制二进制模式:
ifstream file("big.bin", ios::binary); - 显式关闭同步:
file.sync_with_stdio(false);(虽对seekg影响小,但避免后续读取性能抖动) - 确认未设置
skipws:file.unsetf(ios::skipws);(文本模式下尤其重要)
用 tellg() 获取大小时要注意返回类型和符号问题
tellg() 返回 streampos,本质是带符号整数(通常是 long long 或 int64_t),但文件大小是无符号概念。若文件大于 2GB 且编译器把 streampos 实现为有符号 32 位,就可能溢出变负。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 始终用
static_cast<off_t>(file.tellg())</off_t>或static_cast<uint64_t>(file.tellg())</uint64_t>转换,别直接赋给size_t(Windows 上size_t是 32 位) - Linux/macOS 推荐用
off_t(来自<sys></sys>),它保证能存下stat.st_size - 检查是否为 -1 再转换,否则负值转无符号会成极大正数
真正想测“可读体积”,seekg 只是起点
文件大小 ≠ 实际可读字节数。比如文件被其他进程截断、权限变化、NFS 缓存延迟,或者你只开了只读但文件中途被删(Unix 下仍可读完已打开的 inode)。单纯 seekg + tellg 只反映打开瞬间的逻辑长度。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 如果目标是“确保能一口气读完”,在
seekg得到大小后,用file.read(buf, size)并检查file.gcount() == size,这才是真实可用长度 - 对超大文件(>10GB),优先用内存映射(
mmap/CreateFileMapping),避免seekg在机械盘上物理寻道耗时 - 别依赖多次
seekg测速——硬盘寻道时间波动大,单次结果没意义
真正难的不是怎么调 seekg,是怎么判断“这个长度现在还有效”。文件系统、权限、并发修改,这些都比函数调用本身更常出问题。



















