Go fmt 输出与格式化

fmt 输出函数由两个独立维度组合——写到哪(stdout 还是任意 io.Writer)与怎么格式化(默认还是占位符动词);%w 建链、%v 断链,错误分流走 os.Stderr。

#type / concept #status / growing #tech / dev #resource / go #package / fmt

[!info] 关联笔记

Go fmt 输出与格式化

这个概念为什么会出现

打印到屏幕、写错误、把错误包进链、把数据写进任意流——这些事如果每个函数都自己撸一遍字节缓冲,代码又碎又难测。Go 用 fmt 包统一了「格式化 I/O」:输出到哪里、怎么格式化两个维度正交,组合出一族函数。

核心动机是可注入 + 可测:正是因为 Fprintln(w io.Writer, ...) 的第一个参数是接口,测试才能把 bytes.Buffer 塞进去断言输出,而不是真去碰 stdout。

一句话定义

fmt 把「输出目标(stdout / 任意 Writer)」与「格式化方式(默认 / 占位符动词)」两个正交维度组合成一组函数;ln 后缀表示自动补换行,f 后缀表示用占位符动词。

核心机制:两个独立维度

不要背六个孤立函数,按两维理解:

  1. 写到哪
    • 不带 F:写标准输出 os.Stdout(如 Println
    • F(File/Writer):写你传进去的任意 io.Writer(如 Fprintln(w, ...)
  2. 怎么格式化
    • 不带 f:默认格式(等价于 %v,参数间自动加空格)——Print/Println
    • f(format):按占位符模板格式化——Printf/Fprintf,需自己写 \n

组合矩阵:

函数写到格式末尾换行
Printstdout默认
Printlnstdout默认,参数间加空格
Printfstdout占位符模板否(自写 \n
Fprint指定 Writer默认
Fprintln指定 Writer默认,加空格
Fprintf指定 Writer占位符模板

Sprint/Sprintln/Sprintf 同构,只是返回 string 而非写出。

占位符动词(verbs)

Go 的动词直接继承自 C 的 printf 家族,%d 就是十进制整数:

动词含义
%d十进制整数
%s字符串
%f浮点(定点)
%v值的默认格式,通吃
%T值的类型
%#vGo 语法形式(调试神器)
%wErrorf 用,把错误包进链

宽度/精度同 C:%5d%.2f%-5s(左对齐)。%v 是 Go 比 C 方便处——不必纠结该用 %d 还是 %s

[!abstract] 关键区别:%w 建链,%v 断链 fmt.Errorf("scan: %w", err)err 包进错误链errors.Is/As 能顺着 Unwrap 一路比对;用 %v 只把错误拼进字符串,链断,再也比对不到原始错误。要保留链用 %w

最小可运行示例

// 写到 stdout,默认格式,带换行
fmt.Println("line count", n)          // line count 2

// 写到 stdout,占位符,无自动换行
fmt.Printf("count=%d\n", n)           // count=2

// 写到任意 Writer(这里是 stderr),可测
fmt.Fprintln(os.Stderr, "err:", err)  // 错误分流到 stderr

// 占位符建链
err2 := fmt.Errorf("read: %w", err)
fmt.Println(errors.Is(err2, err))     // true

边界与易混淆点

  1. Printf 不自动换行——忘记 \n 输出会粘行;想省事用 Println
  2. FprintlnF 是 Writer 不是 flush——Fprintln(os.Stderr, ...) 只是换个出口写,和 stdout 平级。
  3. 错误走 os.Stderr 是 Unix 管道约定——正常结果走 stdout,错误走 stderr,这样 prog file | sort 只传递正常数字,错误提示不污染数据流。
  4. ln 在不同语境是不同东西——Go Printlnln = line(换行);Unix 命令 ln = link(链接),别混。
  5. 占位符与实参类型不匹配——Go 不 panic,打印 %!d(string=hi) 友好提示(比 C 安全),但 go vet 能提前揪出明显错配。

设计动机

  1. 正交组合:目标与格式解耦,6 个函数覆盖绝大多数场景,不必各写一套。
  2. 可注入F 系列认 io.Writer,业务函数得以脱离 stdout、直接测。
  3. 错误即值Errorf("%w") 让错误像数据一样被包裹、被比对,而非异常抛出。

自测题

  1. fmt.Fprintln(os.Stderr, x)fmt.Println(x) 多了什么语义?
  2. 为什么测试里用 Fprintln(&bytes.Buffer, ...) 而不是 Println
  3. fmt.Errorf("%v", err)%werrors.Is 上的结果有何不同?
答案
  1. 写向 stderr 而非 stdout,且 F 系列可写任意 Writer;错误分流不污染管道数据。
  2. Println 写固定 stdout,测试无法捕获断言;Fprintln 写进内存 Buffer,可直接 out.String() 比对。
  3. %v 断链,比对不到原始错误;%w 建链,errors.Is 为 true。

延伸阅读

资料类型支撑
Package fmt标准库输出与格式化
[[go-error-wrapping错误包装]]本库
[[go-io-reader-writerReader/Writer]]本库
创建于 2026/7/28 更新于 2026/7/28