二进制文件按比例分割须以"rb"模式打开,用fseek+ftell获取真实字节长度;切片大小整数截断计算,fwrite参数顺序勿颠倒;合并时依赖gcount()而非eof()判断读取完成;跨平台路径与文件操作应统一使用std::filesystem;大文件(>512MB)高频合并可考虑内存映射。

按比例分割大文件时,fseek 和 ftell 容易出错
直接用 fseek 计算偏移量再切分,对二进制文件是可行的,但必须确保文件以 "rb" 模式打开;用 "r"(文本模式)在 Windows 下会因换行符转换导致 ftell 返回值失真,最终切片位置错位。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 始终用
fopen(filename, "rb")打开待分割文件 - 先调用
fseek(fp, 0, SEEK_END)+ftell(fp)获取真实字节长度,避免stat()跨平台兼容问题 - 按比例计算切片大小时,用
size_t chunk_size = total_size * ratio,然后向下取整(floor不必要,整数截断即可),剩余字节归入最后一块 - 每块写入新文件时,用
fwrite(buf, 1, actual_read, out_fp),别用fwrite(buf, actual_read, 1, ...)—— 参数顺序反了会写失败
合并多个分片时,std::ifstream 的 read 需要手动检查 gcount()
用 read() 读取分片内容后,不能只靠 eof() 判断是否读完:最后一次读可能刚好处在文件末尾,read() 失败但 gcount() 仍返回上一次成功读取的字节数,导致重复写入或漏写。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每次
read(buf, buf_size)后,立即用stream.gcount()获取实际读取字节数 - 仅当
gcount() > 0时才调用write(),否则跳出循环 - 不要依赖
!stream.eof()做循环条件,它在读失败后才置位,已晚一步 - 分片文件名若含序号(如
part_001.bin),建议用std::sprintf或std::format(C++20)生成,避免手拼字符串出错
跨平台处理路径和文件名时,std::filesystem 是唯一可靠选择
手动拼接路径(如 dir + "/" + name)在 Windows 下会因反斜杠失效;用 _mkdir 或 mkdir 创建目录时,前者只支持 Windows,后者在 macOS/Linux 下不接受多级路径。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- C++17 起,统一用
std::filesystem::create_directories(path)创建嵌套目录 - 构建路径用
std::filesystem::path拼接:std::filesystem::path base("data"); auto p = base / "part_001.bin"; - 获取文件大小用
std::filesystem::file_size(p),比fseek+ftell更简洁且线程安全 - 若需兼容 C++14 及更早版本,只能用
boost::filesystem,标准库无替代方案
内存映射(mmap)不适合小比例切分,但对超大文件合并有明显优势
对几十 MB 级别的文件,用 mmap(Linux/macOS)或 CreateFileMapping(Windows)反而增加复杂度和错误点;但处理上百 GB 文件时,逐块 read/write 会因频繁系统调用拖慢速度,且难以控制缓冲区行为。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 仅当单个分片 > 512MB 且合并频次高时,才考虑内存映射
- Linux 下用
mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0),记得munmap和close(fd)配对 - Windows 下需先
CreateFile+CreateFileMapping+MapViewOfFile,步骤多、错误码分散,务必检查每步返回值 - 内存映射不改变文件逻辑结构,合并时仍需按顺序拷贝映射区域到目标文件,不可直接
memcpy到未分配空间
实际做这件事最麻烦的不是算法,而是不同平台下文件 I/O 行为的细微差异——比如 Windows 文本模式的换行符处理、macOS 对 MAP_NORESERVE 的忽略、Linux mmap 对齐要求等。这些细节不会报错,但会让切出来的文件少几个字节,或者合并后校验和对不上。


















