直接用std::ifstream::getline读gb文件会卡死,因默认4kb缓冲区导致频繁系统调用和string扩容,百万行下堆碎片与内核切换开销剧增;超长行引发反复拷贝,破坏cpu缓存;文本模式使tellg()偏移错乱;预分配+read()最快但需处理stat失败、空字符串取址及read中断重试。

直接用std::ifstream::getline读GB文件会卡死,别这么干
因为默认缓冲区只有4KB,每次getline()都可能触发一次系统调用+一次std::string扩容(1.5x或2x),GB级文件里上百万行,堆碎片和内核态切换开销会让速度断崖式下跌。更糟的是,遇到超长行(比如嵌套JSON日志),std::string反复拷贝已读内容,CPU缓存全废。
- 别信
file >> str或getline(file, str)能“自动优化”——它不会预估行长,也不会复用缓冲区 - 文本模式(默认)在Windows下把
\r\n转成\n,导致tellg()偏移错乱,分页定位失效 - 哪怕加了
file.rdbuf()->pubsetbuf(buf, size),libc仍可能绕过它做自己的缓冲,不可控
预分配+read()比ifstream快15–30%,但要注意三个坑
用stat()拿到文件大小后,std::string::resize()预分配内存,再用open()/read()直写内存,绕过流层封装。这是最稳的全量读取方案。
-
stat()必须检查返回值:NFS、/proc、某些容器挂载点不保证st_size有效,失败时得 fallback 到分块读 - 取可写地址必须用
&str[0],不是str.data()——空字符串时data()可能返回nullptr,而resize()后&str[0]一定合法 -
read()可能被信号中断或提前EOF,必须循环直到读满:while (total
mmap适合只读+随机访问,顺序扫描反而可能更慢
映射后指针访问,零拷贝、O(1)跳转,但首次访问每页都会触发缺页中断。对纯顺序扫描的GB日志,mmap未必比read()快,还多一层SIGBUS风险。
- Linux用
mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0),Windows必须用CreateFileMapping()+MapViewOfFile(),二者偏移对齐要求不同(Windows要64KB倍数,Linux只要4KB) - 映射后别用
strlen()或std::string(data, size)——文本文件没结尾\0,会越界读 - 找换行符用
memchr(p, '\n', end - p),比单字节循环快一个数量级;末尾无\n时,最后一段需手动切std::string_view(p, end - p)
分块读取是通用解法,64KB缓冲区够用
既不用预估文件大小,也不依赖mmap支持,read()配固定缓冲区是最易移植、最不易翻车的方案。
- 缓冲区大小设为
65536(64KB):太小(如4KB)syscall过多,太大(如1MB)浪费cache且无收益 - 必须用二进制模式打开:
std::ifstream file("log.txt", std::ios::binary),否则Windows下\x1A被当EOF - 别用
while (!file.eof())包裹read()——最后一次读失败后eof()才置位,会多执行一次无效循环;正确写法是while (file.read(buf.data(), size)) { ... } if (file.gcount() > 0) { ... }
预分配和mmap看着炫技,但真正上线跑着不崩的,往往是那个老老实实read()循环+64KB缓冲的版本——尤其当你不确定目标机器的内存、内核版本、文件系统类型时。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











