不能直接用fread一次性读完大文件因内存有限易触发OOM或卡死;应分块读取,推荐64KB缓冲区配合read()和gcount()获取真实字节数,必须用二进制模式打开。

为什么不能直接用 fread 一次性读完大文件
因为内存有限,尤其在嵌入式、服务端流式处理或网络上传场景下,把几个 GB 的文件全塞进 std::vector<char></char> 会触发 OOM 或卡死。分块本质是控制单次内存占用,不是“为了分而分”。fread 本身支持分块,但 C++ 标准库的 std::ifstream 默认缓冲区小(通常 4KB–8KB),且不暴露底层 chunk 控制权,容易误以为它“自动分块”——其实只是读得慢,不是按你想要的粒度读。
用 std::ifstream::read 手动控制块大小最稳妥
这是标准、跨平台、无需第三方依赖的做法。关键点不是“怎么读”,而是“怎么确保每次读到预期字节数”。read() 不保证填满缓冲区,它只承诺最多读取指定长度,实际读多少得看文件剩余、磁盘状态、EOF 是否提前到来。
常见错误:忽略 gcount() 返回值,直接拿整个 buffer 当有效数据用。
- 永远在
read()后调用ifs.gcount()获取真实读取字节数 - 缓冲区大小建议设为 64KB(
65536)或 1MB(1048576)——太小增加系统调用开销,太大无必要 - 用
ifs.peek() == EOF判断是否读完,比!ifs更早发现末尾
std::ifstream ifs("large.bin", std::ios::binary);
ifs.seekg(0, std::ios::end);
size_t file_size = ifs.tellg();
ifs.seekg(0);
const size_t CHUNK_SIZE = 65536;
std::vector<char> buf(CHUNK_SIZE);
while (ifs.read(buf.data(), CHUNK_SIZE)) {
size_t n = ifs.gcount(); // 真实读取数
process_chunk(buf.data(), n);
}
// 处理最后一次不足 CHUNK_SIZE 的情况
if (ifs.gcount() > 0) {
size_t n = ifs.gcount();
process_chunk(buf.data(), n);
}
遇到 mmap 比 read 快很多?先确认是不是真需要
mmap 在 Linux/macOS 上确实能绕过内核拷贝,对超大只读文件随机访问友好。但它不是银弹:
立即学习“C++免费学习笔记(深入)”;
- Windows 上对应的是
CreateFileMapping,API 完全不同,跨平台成本高 - 映射失败时(如地址空间碎片化)会静默回退或抛异常,不如
read可控 - 如果只是顺序读一遍再丢弃,mmap 带来的 TLB 压力可能反而拖慢整体吞吐
-
std::ifstream不支持 mmap,必须用 POSIX/Win32 原生 API
除非你明确在做内存映射加速的 benchmark,或已有成熟 mmap 封装,否则别为了“听起来快”换方案。
注意 std::ios::binary 和换行符陷阱
文本模式(默认)在 Windows 下会把 \r\n 自动转成 \n,导致 gcount() 和实际文件字节数对不上,分块错位。哪怕你读的是 JSON 或日志,只要关心原始字节(比如计算 hash、校验 CRC、解析协议头),就必须加 std::ios::binary。
另一个坑:seekg() 在文本模式下行为未定义,所以获取文件大小前务必先打开为 binary 模式。
还有人用 getline() 分块——那是按行,不是按字节块,完全不符合“分块读取”的原始诉求,慎用。


















