os.truncate仅能缩容文件长度,无法截断开头或中间;需配合seek和write实现删开头、删中间等操作,且不可用writefile替代流式覆写。

os.Truncate 只能缩容,不能“截断中间”或“保留末尾”
Go 的 os.Truncate 本质是设置文件逻辑长度——它不是剪刀,而是尺子。调用 f.Truncate(1024) 后,无论原文件多长,结果都是前 1024 字节保留、其余全部丢弃;它不关心当前文件指针在哪,也不会移动指针。想“删掉开头 100 字节后重写”,直接 Truncate 不行,必须配合 Seek 和 Write。
要“截断开头再重写”,得先读出有效内容,再覆写+截断
典型场景:日志文件轮转时,想丢弃最老的 N 行(即开头部分),把剩余内容前移,再追加新日志。这时不能靠 Truncate 简单缩容,因为你要保留的是“末尾数据”。正确流程是:
- 用
os.Open打开原文件,bufio.Scanner或io.ReadFull逐行/按块读取,跳过要丢弃的开头部分,把剩余内容暂存到[]byte或临时bytes.Buffer - 用
os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0)以写+清空模式重新打开(注意:os.O_TRUNC会立刻清空整个文件) - 将暂存的剩余内容
Write回去 - 如果新内容比原文件短,
Write后无需额外Truncate——O_TRUNC已确保文件从头开始写;但如果中途出错、只写了一半,就得在出错后手动f.Truncate(int64(n))收尾,避免残留脏数据
想“删中间”?Seek + Write 覆盖 + Truncate 收尾是唯一可靠路径
比如写入 header + body 时失败,需回滚到 header 结束位置。这不是 Truncate 能解决的,必须手动控制偏移量:
- 写入前记下起始偏移:
startOff, _ := f.Seek(0, io.SeekCurrent) - 出错后,
f.Seek(startOff, io.SeekStart)回到起点 - 用
f.Write([]byte{0})或重复写零字节覆盖已写入的脏数据(可选,取决于格式是否允许残留) - 最后
f.Truncate(startOff)把文件长度精确收回到原始位置 - 注意:若覆盖区域跨磁盘块,
Write长度必须 ≥ 待清除区域长度,否则后续读逻辑可能误解析残留字节
别用 ioutil.WriteFile 替代流式覆写
os.WriteFile 是原子写入:它内部创建临时文件 → 写入 → os.Rename 替换原文件。这会导致文件 inode 改变,硬链接失效,且无法实现“就地截断+续写”。如果你依赖文件句柄持续监听(如 inotify)、或需保持 fd 不变,就必须用 os.OpenFile + Seek + Write + Truncate 这套组合。临时文件方案看似简单,但在某些部署环境(如容器挂载卷、NFS)上 rename 可能失败或不原子。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











