最稳妥做法是复用文件句柄并搭配bufio.writer:用os.openfile指定os.o_create|os.o_wronly|os.o_append(或os.o_trunc)打开文件,创建bufio.newwriter后逐行writestring+writebyte('\n'),结束前必须调用flush()确保数据落盘。

用 os.OpenFile 配合 bufio.Writer 写入多行最稳妥
直接用 os.WriteFile 或 fmt.Fprintln 往文件写多行容易丢数据或换行异常,尤其在循环中反复打开/关闭文件时性能差还易出错。正确做法是复用一个文件句柄,搭配缓冲写入器。
关键点:
-
os.OpenFile要传os.O_CREATE | os.O_WRONLY | os.O_APPEND(追加)或os.O_TRUNC(覆盖),别漏掉os.O_WRONLY -
bufio.NewWriter的缓冲区默认 4KB,每行调用wr.WriteString()+wr.WriteByte('\n')比fmt.Fprintln更可控 - 必须在结束前调用
wr.Flush(),否则最后一行可能滞留在缓冲区不落盘
file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
wr := bufio.NewWriter(file)
defer wr.Flush() // 注意:这里 defer 不会 panic,但没它就可能丢数据
for _, line := range []string{"hello", "world", "golang"} {
wr.WriteString(line)
wr.WriteByte('\n')
}
fmt.Fprintln 能不能直接用?能,但要注意文件打开方式
可以,但前提是文件句柄必须是可写的、且未被其他 goroutine 并发写入。常见错误是误用 os.Create 后又用 fmt.Fprintln,结果每次调用都覆盖全文件。
典型误用场景:
- 在循环里反复调用
os.Create→ 每次都清空重写 - 用
os.Open(只读)打开文件后调fmt.Fprintln→ 报bad file descriptor - 忘记检查
fmt.Fprintln返回的 error → 某些磁盘满或权限问题下静默失败
安全写法示例:
f, _ := os.OpenFile("out.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
defer f.Close()
fmt.Fprintln(f, "first line")
fmt.Fprintln(f, "second line") // 自动加 \n
按行写入时换行符在 Windows 和 Linux 上有差异吗?
Go 的 \n 在所有平台都写作 LF(0x0A),不会自动转成 CRLF。如果你需要兼容 Windows 记事本(否则可能显示为一行),得手动补 \r\n。
但绝大多数现代工具(VS Code、Git、Linux 终端)都认 LF,强行加 \r 反而可能在 Unix 环境下造成多余空行或 ^M 符号。
建议:
- 日志、配置、数据交换类文本 → 坚持用
\n - 明确交付给 Windows 用户双击打开的报告文件 → 用
wr.WriteString(line + "\r\n") - 不确定时查目标系统:运行
runtime.GOOS判断,但通常没必要
大文件按行写入卡顿?可能是缓冲区太小或没批量刷盘
写几万行时如果每行都 wr.Flush(),性能会断崖式下跌;但完全不 flush 又怕崩溃丢数据。折中方案是定期 flush,比如每 100 行刷一次。
更稳的做法:
- 把
bufio.NewWriterSize(file, 64*1024)缓冲区设大些(如 64KB) - 每 N 行调一次
wr.Flush(),N 根据行平均长度估算(例如平均 100 字节,1000 行 ≈ 100KB) - 避免在循环内新建
strings.Builder或拼接长字符串,直接往bufio.Writer写
真正容易被忽略的是:即使用了 bufio.Writer,如果底层文件是 NFS 或某些加密卷,Flush() 仍可能阻塞较久——这时要考虑加超时或异步落盘,但那就超出“按行写入”的基本范畴了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











