fmt.printf占位符需按类型严格匹配:整数用%d,浮点用%f或%g,字符串调试用%v、原始内容用%q;修饰符顺序为flags.[precision]verb;sprintf格式错误返回含%!的脏字符串,需校验;自定义类型应实现stringer接口控制%v输出。

fmt.Printf 常见占位符怎么选,不乱码也不丢精度
Go 的 fmt.Printf 不是 Python 的 f-string,也不是 JavaScript 的模板字面量——它靠类型+动词严格匹配。用错动词,比如对 float64 用 %d,会直接 panic:fmt: %d verb not supported by float64;对中文字符串用 %s 没问题,但用 %q 就会多出引号和转义,不是所有场景都想要。
实操建议:
-
%v最安全,适合调试:自动适配类型,结构体、切片、map 都能展开,但不控制精度和格式 - 数字输出优先用
%d(整数)、%f(浮点,默认小数点后6位)、%g(自动选%e或%f,省略末尾零,适合通用数值) - 字符串想看原始内容(含换行、制表符)用
%q;想确认是否为空或 nil,%v比%s更可靠(nil字符串用%s会 panic) - 十六进制打印
[]byte用%x(小写)或%X(大写),别用%d——那会打一串数字而非字节序列
宽度、精度、标志这些修饰符怎么加才不反直觉
Go 的格式化修饰符顺序固定:[flags][width].[precision]verb,漏掉中括号里的某一项,含义就全变。比如 %6.2f 是“总宽6、小数2位”,但 %.2f 是“不限总宽、只控小数2位”;而 %6f 是“总宽6、小数默认6位”,不是“小数6位、总宽不限”。
常见踩坑:
立即学习“go语言免费学习笔记(深入)”;
-
-标志左对齐,但对数字默认右对齐,加了-后负号会贴最左,比如%-10d打印-42会变成-42(空格在右),不是-42 -
0标志只对数字生效,且仅填充前导零;对字符串无效,%010s等价于%10s - 精度对字符串是最大输出长度(截断),对浮点是小数位数,对 slice/map 是最大元素数——同一修饰符,语义随动词变
- 宽度为
*表示从参数取值,如fmt.Printf("%*s", 10, "hi"),但必须保证参数顺序匹配,否则 panic
fmt.Printf 和 fmt.Sprintf 在错误处理时行为差异很关键
fmt.Printf 直接输出到 stdout,失败极少(除非 stdout 被关闭);fmt.Sprintf 返回字符串,但一旦格式错误(如动词与参数类型不匹配),它不会 panic,而是返回格式串本身 + 错误描述,比如 fmt.Sprintf("%d", "hello") 返回 "%!d(string=hello)"。这容易掩盖问题——你可能以为拼接成功了,实际得到的是带 %! 的脏字符串。
实操建议:
- 单元测试里别只检查
fmt.Sprintf返回值是否非空,要验证是否含%!或invalid - 生产日志中若看到类似
"user=%!s(MISSING)",说明传了 nil 接口或类型不匹配,不是数据为空 - 需要强类型保障?考虑用
fmt.Sprint/fmt.Sprintln替代Sprintf,它们不解析动词,纯拼接,没格式风险
自定义类型怎么让 fmt.Printf 输出得体,而不是 {0xc000010230}
默认打印结构体指针就是内存地址,因为 Go 不知道你想展示什么。要控制输出,必须实现 Stringer 接口(即 func (T) String() string),但注意:这个方法只影响 %v、%s、%q,不影响 %d 或 %x——后者仍会报错“verb not supported”。
关键细节:
- 如果结构体字段含指针或嵌套,
String()方法里别直接递归调用fmt.Sprintf("%v", s.field),可能引发无限循环(比如链表节点) - 想支持多种格式(如
%+v显示字段名),得自己解析fmt.State和fmt.Verb,实现fmt.Formatter接口,比Stringer复杂得多 - 第三方库如
github.com/davecgh/go-spew/spew可临时替代,但上线别用——它不保证性能,且会暴露未导出字段
真正难的不是记住所有动词,而是每次写 fmt.Printf 时,心里得过一遍:这个变量此刻是什么类型、有没有可能为 nil、下游系统是否对空格/引号敏感——格式化不是装饰,是契约。











