不能直接用 fseek + fwrite 在文件开头插入数据,因为文件系统不支持插入操作,fwrite 会覆盖而非移动原有内容;正确做法是读取全文件到内存(如 vector),在开头插入新数据,写入临时文件后原子替换原文件。

为什么不能直接用 fseek + fwrite 在文件开头插入数据
因为文件系统不支持“在中间或开头插入”——磁盘上文件是连续(或逻辑连续)的字节流,fseek 只能定位,fwrite 写入时会覆盖当前位置起的原有字节。想“插入”,本质是“把原内容整体后移”,而标准 C/C++ 文件 API 没有原子化的“插入”操作。
最稳妥的做法:内存流 + 临时文件重写
核心思路不是“修改原文件”,而是“读出全部内容 → 拼接新头部 → 写入新文件 → 原子替换”。C++ 中用 std::stringstream 或 std::vector<char></char> 做内存中重组,避免逐块搬移的边界错误。
- 先用
std::ifstream以std::ios::binary模式读完整个原文件到std::vector<char></char> - 构造新数据:把要插入的头部字节(如 BOM、magic number、元信息)插入到该
vector开头 - 用
std::ofstream写入临时文件,再用std::filesystem::rename原子替换原文件(注意:Windows 下需先remove原文件,否则 rename 失败) - 务必检查每次 I/O 的
fail()和bad()状态,尤其大文件读取可能因内存不足失败
std::stringstream 不适合大文件插入的真相
它底层依赖 std::string,频繁 str().insert(0, ...) 会触发多次内存重分配和整块拷贝,时间复杂度 O(n²)。10MB 文件插入 1KB 头部,可能拷贝上百 MB 数据。
- 对小文件(std::stringstream 写法简洁:先
ss - 对大文件,必须改用
std::vector<char></char>预分配空间:vec.reserve(header_size + original_size);,再用insert(vec.begin(), header.begin(), header.end()) - 别用
std::ostringstream+str()获取结果——str()返回的是拷贝,又多一次分配
跨平台原子替换的坑:Windows vs Linux
std::filesystem::rename 在 Linux/macOS 上可直接覆盖同名文件;但在 Windows 上,若目标文件已存在,会抛出 std::filesystem::filesystem_error。
立即学习“C++免费学习笔记(深入)”;
- 安全写法:先
std::filesystem::remove("original.txt")(忽略不存在错误),再rename("temp.txt", "original.txt") - 更健壮的做法:生成唯一临时名(如带 pid + timestamp),避免并发写冲突
- 不要用
std::remove(C 标准库)替代std::filesystem::remove,前者不保证删除只读文件(尤其 Windows) - 如果程序可能被中断(如崩溃),建议在替换前把原文件备份为
original.txt.bak



















