跳转到主要内容
模块 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 error

Print 和 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)。
%qGo 风格的引用字符串(包括转义符)。
%cUnicode 字符 (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)
答案选项