分块读写推荐64KB缓冲区;必须显式设置ios_base::binary且打开时指定;错误需分层处理;mmap仅在内存充足且单次冷启动时优于分块读写。

用 std::ifstream 和 std::ofstream 分块读写时,缓冲区大小设多少合适
默认流缓冲区(std::streambuf 内部)通常只有几百字节,对大文件复制就是灾难——系统调用太频繁,read()/write() 次数爆炸,CPU 花在上下文切换上比实际搬运数据还多。
实操建议:直接绕过流缓冲,用自定义缓冲区 + readsome() 或 read() + write(),缓冲区大小选 64KB(65536)或 128KB 是多数场景的甜点:
-
65536兼容性好,几乎所有平台、文件系统、磁盘队列深度都能吃住 - 再大(如
1024*1024)在某些嵌入式或低内存环境可能触发分配失败,或让 page cache 压力陡增 - 别迷信“越大越好”——Linux 的
copy_file_range()在内核态做零拷贝时,反而对 128KB~1MB 更友好,但 C++ 标准库不暴露该接口
为什么 ios_base::binary 必须显式设置,且不能只设一次
不加 ios_base::binary,Windows 下会把 \x0A 自动转成 \x0D\x0A,Linux/macOS 虽不转换,但部分 libc 实现仍可能因换行符检测逻辑引入额外开销;更隐蔽的问题是:一旦流打开后修改 binary 模式,std::fstream 不保证行为可预测——有些实现会忽略后续 setf() 调用。
实操建议:打开文件时就定死模式,不要复用流对象跨文本/二进制场景:
立即学习“C++免费学习笔记(深入)”;
- 用
std::ifstream src{path, std::ios_base::binary},不是std::ifstream src; src.open(path, std::ios_base::binary) - 同理,目标文件也必须用
std::ofstream dst{dst_path, std::ios_base::binary | std::ios_base::out} - 如果要复用同一个
std::ofstream对象写多个文件,每次open()前必须先close(),再带完整标志重开
遇到 failbit 或 badbit 后,怎么安全继续而不是崩溃
大文件复制中,read() 返回 0 表示 EOF,但返回负值或 !src.good() 触发时,可能是磁盘满、权限丢失、NFS 连接中断等真实错误——此时若直接 throw 或 exit(),上层没法清理临时文件或回滚状态。
实操建议:按错误类型分层处理,不依赖 exceptions() 全局开关(它会让所有流操作都抛异常,得不偿失):
- 检查
src.gcount() == 0 && src.eof()→ 正常结束,跳出循环 - 检查
src.fail()且!src.bad()→ 可能是格式问题(比如误开了文本模式),尝试clear()并跳过当前块(慎用,仅限已知可恢复场景) - 检查
src.bad()或dst.fail()→ 立即停止,dst.close(),返回错误码,由调用方决定是否删除已写入的目标文件
用 mmap 替代流读写真的更快吗?什么情况下反而更慢
对超大文件(>1GB),mmap + memcpy 看似零拷贝,但实际受制于物理内存和 page fault 开销:首次访问 mmap 区域会触发缺页中断,若文件远大于可用 RAM,内核疯狂 swap,性能断崖下跌。
实操建议:只在明确满足以下条件时考虑 mmap:
- 目标平台确定支持
MAP_POPULATE(Linux)或FILE_MAP_LARGE_PAGES(Windows),能预加载物理页 - 文件大小 ≤ 可用空闲 RAM 的 70%,且进程无其他大内存占用
- 复制是单次、冷启动场景(比如安装程序解压),而非高频小文件批量处理
- 否则,老老实实用 64KB 分块 +
read()/write(),稳定性和可预测性高得多
真正容易被忽略的是:哪怕用了 mmap,写入目标文件仍需 msync() 或 munmap() 触发落盘,而标准流的 close() 会隐式 flush —— 这个同步时机差,可能让“快”的假象在数据持久化层面崩掉。











