fmt.fprintf 是 go 中将格式化字符串直接写入 io.writer(如文件)最直接高效的方式,避免中间字符串分配;需注意参数顺序、类型匹配、%% 转义百分号,并优先用 encoding/csv 处理 csv 等复杂格式。

用 fmt.Fprintf 写入带格式的字符串最直接
Go 没有像 Python 的 f-string 或 C 的宏式格式化,但 fmt.Fprintf 是写入自定义格式数据最常用、最可控的方式。它把格式化结果直接写入任意 io.Writer(比如文件、网络连接、bytes.Buffer),避免中间字符串拼接开销。
常见错误是先用 fmt.Sprintf 拼出完整字符串再写入——这会多一次内存分配和拷贝,尤其在高频日志或批量导出时明显拖慢性能。
-
fmt.Fprintf(w, "user:%s,id:%d,ts:%.3f\n", u.Name, u.ID, time.Since(start).Seconds())—— 直接写入,不生成中间字符串 - 注意字段顺序必须和参数一一对应;类型不匹配(比如用
%d传string)会在运行时报panic: fmt: %!d(string=xxx) - 如果格式中含百分号字面量,写两个:
%%,否则会被误认为格式动词
写结构体时优先用 encoding/csv 而非手撕逗号分隔
想把 []User 写成 CSV 表格?别用 fmt.Fprintf 手动加引号、转义双引号、处理换行——CSV 规范比直觉复杂得多,容易在含逗号、换行或双引号的字段上出错。
encoding/csv 包自动处理这些边界情况,且支持自定义分隔符、引用字符和编码。
- 写入前调用
w.Write([]string{"name", "email", "score"})写表头 - 每行数据用
w.Write([]string{u.Name, u.Email, strconv.Itoa(u.Score)}),不用管引号和转义 - 分隔符不是逗号?用
csv.NewWriter(f).Comma = '\t'改成制表符 - 别忘了最后调用
w.Error()检查写入是否真正成功(比如磁盘满、管道断开等底层错误)
需要二进制或紧凑格式?encoding/gob 比 JSON 更省空间
如果目标是 Go 程序之间高效交换数据(比如 RPC 响应缓存、本地持久化),gob 是比 JSON 更轻量的选择:它原生支持 Go 类型,不序列化字段名,整数/浮点数按原生字节写入,体积通常小 30%–50%。
但注意:gob 不兼容其他语言,且要求结构体字段首字母大写(可导出)才能被编码。
- 写入前必须注册所有可能用到的类型:
gob.Register(User{}),否则解码时 panic - 不能直接写指针值:
enc.Encode(&u)可以,但enc.Encode(u)也可以(只要User是可导出结构体) - 文件末尾没有换行或分隔符,所以多个
Encode连续调用后,得靠Decode逐个读取,不能 seek 或跳过
自定义格式需解析时,务必实现 String() string 和 UnmarshalText
如果你定义了类似 type Duration time.Duration 这样的自定义类型,并希望它能用 "1h30m" 这种人类可读格式写入/读取(比如配置文件、命令行参数),光靠 fmt.Stringer 不够——json.Unmarshal、flag.Set、yaml.Unmarshal 都会忽略 String() 方法。
必须同时实现 UnmarshalText([]byte) error,否则反序列化会失败或静默丢弃字段。
func (d *Duration) UnmarshalText(text []byte) error { t, err := time.ParseDuration(string(text)); *d = Duration(t); return err }- 写入时仍靠
String(),但读取依赖UnmarshalText;两者返回格式最好一致,否则易混淆 - 如果类型含指针或 map 字段,
UnmarshalText里要负责初始化,否则解码后仍是 nil
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











