Go的fmt库不支持运行时动态格式化,但以类型安全、零分配和清晰动词设计保障可靠性;%v、%+v、%#v分别用于值、带字段名、带类型前缀的结构体输出,%d/%f/%s等动词需严格匹配类型,修饰符影响语义而非仅美化,且需警惕循环引用与分配开销。

Go 的 fmt 库不支持运行时动态格式化字符串(比如 Python 的 f"{x}"),所有动作用的动词和宽度都得写死在格式字符串里;但它的类型安全、零分配优化和清晰的动词设计,反而让多数场景更可靠。
常用动词怎么选:%v、%+v、%#v 用错就丢关键信息
打印结构体时,%v 只输出字段值,%+v 补上字段名(调试必备),%#v 输出带类型前缀的 Go 字面量(可直接复制进测试用例)。切片或 map 为空时,三者都显示 [] 或 map[],但若想区分 nil 和空,得加判断:
if s == nil {
fmt.Println("s is nil")
} else {
fmt.Printf("s: %+v\n", s)
}
-
%d仅用于整数,%f仅用于浮点,混用会 panic(如fmt.Printf("%d", 3.14)) -
%s要求参数是string,传[]byte会报错;需转成string(b)或用%s配合fmt.Printf("%s", b)(fmt内部对[]byte做了特例处理) -
%q对字符串加双引号并转义,对 rune 输出单引号形式('a'),比手写"\""+s+"\""安全
宽度、精度、标志位不是装饰:它们影响输出语义
格式动词里的修饰符不是“美化选项”,而是控制行为的关键部分。例如 %6.2f 表示总宽至少 6 字符、小数点后 2 位(不足补空格,超长不截断);%06d 中的 0 表示用 0 填充而非空格;%-10s 左对齐(默认右对齐)。
- 精度对字符串表示最大字符数(
%.3s截取前 3 字符),对浮点数表示小数位数,对数字表示最小位宽(%05d等价于%5.5d) -
%x和%X打印十六进制,加#标志(%#x)会自动加0x前缀,适合日志中标识内存地址或哈希值 - 对指针用
%p,不要用%x—— 后者可能被优化掉符号位,且不保证跨平台一致
fmt.Sprint/fmt.Sprintf 不总是安全:注意接口值逃逸和循环引用
fmt.Sprint 和 fmt.Sprintf 在底层会调用 reflect 处理任意接口值,一旦结构体字段含自身指针(如链表节点、树节点),%v 会无限递归直到栈溢出或触发内部深度限制(默认 10 层),报错类似 runtime: goroutine stack exceeds 1000000000-byte limit。
立即学习“go语言免费学习笔记(深入)”;
- 避免在日志中无条件用
%+v打印用户传入的任意 struct;先检查是否含循环引用,或改用自定义String()方法 -
fmt.Sprint返回string,但若参数含大 slice 或 map,底层可能触发堆分配;高频路径建议用fmt.Fprint写入预分配的bytes.Buffer - 如果只是拼接几个已知类型变量,优先用
fmt.Appendf(Go 1.22+)或直接字符串连接(s := a + ":" + b),比Sprintf更轻量
真正难的不是记住所有动词,而是意识到 fmt 的每个修饰符都在参与“输出契约”——它决定了日志能否被 grep 解析、调试信息是否可读、序列化结果是否稳定。别把格式字符串当注释写。











