根本区别在于参数处理方式:Print/Println是值导向,直接输出任意数量的值;Printf是格式导向,首参必须为格式字符串,否则panic。

为什么 Print、Println、Printf 输出结果看起来“不一样”
根本区别不在“换行”或“格式”,而在于参数处理方式:Print 和 Println 是“值导向”的,直接输出任意数量的值(支持 interface{});Printf 是“格式导向”的,第一个参数必须是格式字符串,后续参数按顺序填充占位符。
常见误用:把变量直接塞进 Printf 第一个位置,比如 Printf(x) —— 这会触发 panic:fmt: Printf call needs 2+ arguments, but has only 1。因为 Printf 强制要求格式串。
-
Print("hello", 42, true)→ 输出hello42true(无分隔,无换行) -
Println("hello", 42, true)→ 输出hello 42 true\n(空格分隔,末尾自动换行) -
Printf("hello %d %t", 42, true)→ 输出hello 42 true(无自动换行,需手动加\n)
Printf 中 %v、%+v、%#v 到底怎么选
这三个都是通用打印动词,但语义差异明显,尤其在调试结构体时容易混淆:
-
%v:默认值格式,字段名不显示,只输出值,如{1 "abc"} -
%+v:带字段名,适合快速确认结构体内容,如{X:1 Name:"abc"} -
%#v:Go 语法格式,可直接复制粘贴为代码字面量,如main.User{X:1, Name:"abc"}
注意:%#v 对 map/slice 也会输出完整类型信息(如 map[string]int{"a": 1}),但对未导出字段(小写开头)仍不可见 —— 这不是 bug,是 Go 反射机制限制。
立即学习“go语言免费学习笔记(深入)”;
字符串拼接用 fmt.Sprintf 还是 strings.Builder
如果目标是“构造字符串”,别无脑用 Sprintf。它每次调用都分配新内存、做格式解析,性能开销明显。
- 少量、简单拼接(≤3 次,变量少):用
Sprintf更直观,如msg := Sprintf("user %s id %d", name, id) - 高频、循环内拼接(如日志批量生成):必须用
strings.Builder,避免重复 alloc:var b strings.Builder b.Grow(128) b.WriteString("user ") b.WriteString(name) b.WriteString(" id ") b.WriteString(strconv.Itoa(id))
Builder.String() 是零拷贝的(内部 slice 直接转 string),而 Sprintf 总是新建字符串。
中文乱码?os.Stdout 的编码其实不归 fmt 管
fmt 包本身不处理字符编码,它只是把 []byte 写给 os.Stdout。乱码本质是终端/IDE 的编码设置与 Go 字符串(UTF-8)不匹配,或 Windows 控制台默认非 UTF-8。
- Windows 命令行下,先执行
chcp 65001切到 UTF-8 - VS Code 终端:检查右下角编码是否为 UTF-8,不是就点击切换
- 程序里硬编码
os.Stdout.Write([]byte("你好"))和Println("你好")行为一致 —— 都依赖底层 io.Writer 实现
真正要干预输出编码,得替换 os.Stdout,比如用 golang.org/x/text/encoding 包套一层 writer,但绝大多数场景没必要 —— 修复环境比改代码更可靠。










