fmt.fprintf不直接写文件,需传入os.file或bufio.writer;必须检查错误、正确打开文件、defer关闭、适时flush,避免内容丢失或panic。

fmt.Fprintf 本身不直接操作文件,它只负责格式化并写入任意 io.Writer。真正决定“写入文件”的是传给它的那个文件句柄(*os.File)或带缓冲的包装器(如 *bufio.Writer)。用错目标、漏关文件、忽略错误或忘记 Flush,都会导致内容丢失或 panic。
传 *os.File 前必须正确打开并检查错误
不能直接传路径字符串,也不能用只读模式写。常见错误是:os.Open(只读)、os.OpenFile 没加 os.O_WRONLY 或权限掩码错误。
- 写新文件或覆盖:用
os.Create("log.txt")—— 它等价于os.OpenFile("log.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - 追加写:显式用
os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - 务必检查
err != nil;否则后续fmt.Fprintf对nil *os.File会 panic - 记得
defer f.Close(),否则最后一批缓冲数据可能根本没落盘
用 bufio.Writer 提升小量写入性能
频繁调用 fmt.Fprintf(f, ...)(尤其每次只写几字节)会触发大量系统调用,拖慢速度。加一层 bufio.Writer 能自动缓冲,减少 I/O 次数。
- 创建:
w := bufio.NewWriter(f),然后把w传给fmt.Fprintf - 必须在程序退出前调
w.Flush()(通常用defer w.Flush()),否则缓冲区内容永不写出 - 如果写入量大(比如单次 > 4KB),缓冲收益变小,可考虑跳过
bufio直接写*os.File -
bufio.Writer的默认缓冲大小是 4096 字节;可通过bufio.NewWriterSize(f, 16*1024)调整
fmt.Fprintf 返回值必须检查
它返回 (n int, err error),但很多人只用它“顺手写”,忽略 err。而文件写入失败(磁盘满、权限不足、连接中断)时,err 就是真实原因。
- 不要写成:
fmt.Fprintf(w, "msg: %s\n", s)(丢弃返回值) - 至少做基础判断:
if _, err := fmt.Fprintf(w, "msg: %s\n", s); err != nil { log.Printf("write failed: %v", err) } - 对关键日志或配置写入,建议直接
panic或os.Exit(1),避免静默失败 -
n是实际写入字节数,一般等于格式化后字符串长度;若n ,说明写入被截断(极少见,多见于自定义 <code>Writer)
别混用 Fprint 系列和 Sprint 系列逻辑
有人想“先拼好字符串再写”,于是用 fmt.Sprintf 再 io.WriteString,这多一次内存分配,还绕过 fmt.Fprintf 的底层优化。
- 直接写:
fmt.Fprintf(w, "user=%s&id=%d&ts=%d", u.Name, u.ID, time.Now().Unix()) - 避免写:
s := fmt.Sprintf("user=%s&id=%d&ts=%d", u.Name, u.ID, time.Now().Unix()); io.WriteString(w, s) - 只有当你需要复用格式化结果多次(比如同时写文件+发 HTTP),才值得先
Sprintf一次 -
bytes.Buffer是io.Writer,但它本质是内存切片;反复写入时记得buf.Reset(),而不是新建对象
最易被忽略的是:即使你传了正确的 *os.File,也做了 defer Close(),但如果没用 bufio.Writer 且没手动 Flush,或者用了 bufio.Writer 却忘了 defer Flush(),最后一行日志就永远卡在内存里——它不会因为进程退出而自动刷出,除非你用的是 os.Stdout 这类行缓冲终端设备。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











