fmt.Printf适合终端打印,fmt.Sprintf返回字符串用于拼接或后续处理;%+v带字段名调试最常用,%#v输出Go语法字面量;String()方法可自定义%v输出。

fmt 包不是万能的格式化工具,它适合调试和简单输出,不适合高性能日志或结构化数据序列化。
什么时候该用 fmt.Printf,什么时候该用 fmt.Sprintf
fmt.Printf 直接写到 os.Stdout,适合终端打印;fmt.Sprintf 返回字符串,适合拼接、测试或后续处理。别在循环里用 fmt.Printf 打日志——它带 I/O 开销,且无法控制输出目标。
- 调试时快速看值:用
fmt.Printf("val=%v\n", x) - 构造提示文案(比如错误信息):用
msg := fmt.Sprintf("failed to parse %s: %w", filename, err) - 想把格式化结果存进 map 或传给 HTTP 响应:必须用
Sprintf,Printf没有返回值
%v、%+v 和 %#v 的实际差异
三者都用于“默认格式”,但细节决定是否能看清结构体字段:
-
%v:只输出字段值,不带字段名({123 true}) -
%+v:带字段名({ID:123 Active:true}),调试结构体时最常用 -
%#v:输出 Go 语法风格的字面量(main.User{ID:123, Active:true}),适合生成可读的测试用例或反射调试
注意:%+v 对匿名字段或嵌套结构体也生效,但对 map、slice 等内置类型无额外字段名信息。
立即学习“go语言免费学习笔记(深入)”;
为什么 fmt.Println 有时输出空行,而 fmt.Print 不会
fmt.Println 总是在末尾自动加换行符;fmt.Print 完全不加——它只是原样输出参数,连空格都不加(除非你显式传入 " ")。常见误用是混用两者导致多余空行:
fmt.Print("loading...") // 输出:loading...
time.Sleep(1 * time.Second)
fmt.Println("done") // 输出:done + 换行 → 实际看到两行更安全的做法:
- 需要精确控制换行:统一用
fmt.Print+ 显式"\n" - 写日志或用户提示:优先用
fmt.Printf,明确意图(fmt.Printf("loaded %d items\n", n)) - 避免
fmt.Println接多个参数再手动拼空格——它会在参数间加空格,行为不可控
自定义类型如何让 %v 输出更友好
实现 String() string 方法即可。注意:这个方法仅影响 %v、%s、%q 等字符串类动词,不影响 %+v(它仍按字段展开)。
type Status int
const (
Pending Status = iota
Done
)
func (s Status) String() string {
switch s {
case Pending: return "pending"
case Done: return "done"
default: return "unknown"
}
}
// 使用
fmt.Printf("%v\n", Pending) // 输出:pending关键点:
- 方法必须是指针或值接收器均可,但保持一致
- 不要在
String()里调用fmt.Sprint自身,会导致无限递归 - 如果既要
%+v展开字段,又想某些字段隐藏,得用GoString() string(仅被%#v调用)
fmt 的核心价值在于「够快、够简单、够直观」,但它不处理缩进、颜色、多语言或模板逻辑——这些得交给专门库。真正容易被忽略的是:当输出内容含用户输入时,fmt.Printf(userInput, ...) 是严重漏洞,永远用 fmt.Printf("%s", userInput) 做转义。










