模块 8 · 第 1 课(共 5)课程中的36/50~10 min
模块课程(1/5)
高级“fmt”
fmt 包是 Go 中所有文本 I/O 的网关。充分了解它意味着编写可读日志、信息性错误以及惯用的格式化值,而无需求助于外部库。
四个家庭
fmt 公开四个系列的函数,按前缀区分:
Print*→ 写入os.Stdout(Print、Println、Printf)。Fprint*→ 写入任何io.Writer(Fprintln(os.Stderr, ...),Fprintf(file, ...))。Sprint*→ 返回一个字符串而不打印(Sprintf,Sprintln)。Errorf→ 构建格式化的error。
Go
fmt.Println("hello") // stdout
fmt.Fprintln(os.Stderr, "boom") // stderr
s := fmt.Sprintf("[%d]", 42) // "[42]"
err := fmt.Errorf("parse %q: %w", in, base) // wrapped errorPrint 和 Println 之间的区别仅在于参数和尾随换行符之间的空格:Println 始终添加 \n,Print 不添加,Printf 遵循格式。
主要动词
| 动词 | 它有什么作用 |
|---|---|
%v | 默认值(如果实现,则调用 String())。 |
%+v | 具有字段名称的结构。 |
%#v | %#v Go 语法(对于调试和快照很有用)。 |
%T | 值的动态类型。 |
%d | %d以 10 为底的整数。 |
%b %o %x %X | %b %o %x %X二进制、八进制、十六进制(小写/大写)。 |
%f %e %g | %f %e %g浮点:固定、指数、紧凑。 |
%s | %s字符串(或 error/Stringer)。 |
%q | Go 风格的引用字符串(包括转义符)。 |
%c | Unicode 字符 (rune)。 |
%p | 十六进制指针。 |
%w | %w 错误换行(仅在 Errorf 内部)。 |
Go
type Point struct{ X, Y int }
p := Point{1, 2}
fmt.Printf("%v\n", p) // {1 2}
fmt.Printf("%+v\n", p) // {X:1 Y:2}
fmt.Printf("%#v\n", p) // main.Point{X:1, Y:2}
fmt.Printf("%T\n", p) // main.Point宽度、精度、标志
数字和字符串动词接受 宽度 和 精度:
%6d→ 最小宽度 6,左侧填充空格。%-6d→ 宽度 6,右填充(标志-)。%06d→ 宽度 6,用0填充(标志0)。%.2f→ 精度 2 位小数。%8.2f→ 宽度 8,精度 2。
Go
fmt.Printf("|%6d|\n", 42) // | 42|
fmt.Printf("|%-6d|\n", 42) // |42 |
fmt.Printf("|%06d|\n", 42) // |000042|
fmt.Printf("|%8.2f|\n", 3.14)// | 3.14|对于对齐的表,在移动到 text/tabwriter 之前,文本列的固定宽度 + %-...s 是最简单的模式。
%w 和错误包装
%w 很特殊:它仅在 fmt.Errorf 内部工作,并保留链中的原始错误,以便 errors.Is 和 errors.As 可以遍历它。
Go
if _, err := os.Open(path); err != nil {
return fmt.Errorf("read config %q: %w", path, err)
}使用 %v 而不是 %w 打印错误消息,但会破坏链条:没有解开,没有 errors.Is。规则:如果您希望调用者能够区分错误的类型或标识,请使用 %w (每个 Errorf 只能使用一个)。
练习
锻炼#go.m8.l1.e1
尝试:0加载中...
使用 fmt.Sprintf 将整数 42 中的字符串“[42]”格式化,然后打印它。
正在加载编辑器...
3 次尝试后可用的解决方案
锻炼#go.m8.l1.e2
尝试:0加载中...
使用动词 %+v 打印结构体 p(它必须包含字段名称)。
正在加载编辑器...
3 次尝试后可用的解决方案
Quiz#go.m8.l1.e3
准备好fmt.Errorf 中使用的哪个动词保留 error.Is/As 的错误链?
Go
err := fmt.Errorf("read %q: ???", path, base)