Menu

Golang Printf 与 Sprintf:fmt 格式化动词速查表

Go 的 fmt 包如何打印和格式化值:Println、Printf、Sprintf 与 Errorf 的区别,完整的格式化动词表(%v、%+v、%d、%s、%q、%f、%T、%w 等),以及宽度、精度和填充。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

fmt 包有三组打印函数,每组都有相同的三种变体:

函数输出到格式
Print、Println、Printf标准输出默认格式、带空格和换行的默认格式、格式字符串
Sprint、Sprintln、Sprintf返回的 string同样三种风格
Fprint、Fprintln、Fprintf任意 io.Writer(文件、缓冲区、HTTP 响应)同样三种风格
Errorf返回的 error格式字符串,另外支持用于包装的 %w

Println 会在操作数之间加空格,并在末尾加换行。Printf 什么都不加:\n 要你自己写。Print 只在两个相邻操作数都不是字符串时才加空格,这一点出人意料,所以大多数代码都用 Println 或 Printf。

格式化动词

通用

动词打印内容示例输出
%v默认格式的值{Ana 31 [admin]}
%+v带字段名的结构体{Name:Ana Age:31 Tags:[admin]}
%#v值的 Go 语法表示main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}}
%T类型main.User
%%字面意义的百分号%

调试时首选 %+v。指向结构体的指针会打印成 &{...},而不是一个地址。map 打印时按键排序,所以即使 map 的遍历顺序是随机的,输出也是稳定的。

整数

动词含义fmt.Sprintf(verb, 255)
%d十进制255
%b二进制11111111
%o八进制377
%O带 0o 前缀的八进制0o377
%x / %X十六进制,小写或大写ff / FF
%#x带 0x 前缀的十六进制0xff
%c该码点对应的字符ÿ
%q带单引号的字符字面量'ÿ'
%UUnicode 格式U+00FF

浮点数

动词含义fmt.Sprintf(verb, 1234.5678)
%f十进制小数,默认 6 位1234.567800
%.2f十进制小数,2 位1234.57
%e科学计数法1.234568e+03
%g%e 或 %f 中较短的一种,不带末尾的 01234.5678
%v与 %g 相同1234.5678

%.2f 是对浮点数精确的二进制值做舍入,所以 fmt.Sprintf("%.2f", 2.675) 得到 2.67:最接近 2.675 的 float64 比它略小一点。永远不要用浮点数来格式化金额;用整数保存以分为单位的金额。

字符串与字节

动词含义fmt.Sprintf(verb, "go\n")
%s原样输出字符串go 加一个换行
%q带双引号,显示转义字符"go\n"
%x每个字节的十六进制676f0a
% x带空格的十六进制67 6f 0a

对 []byte 使用 %s 会把它当作文本打印;%v 打印的是数字([104 105])。

其他类型

动词类型打印内容
%tbooltrue 或 false
%p指针、切片、map、channel、函数地址,比如 0xc000012345
%werror(仅限 Errorf)错误消息,同时包装该错误

宽度、精度与填充

在 % 和动词之间,可以放标志、宽度和精度:

写法效果
%5d宽度为 5,右对齐(左侧用空格填充)
%-5d宽度为 5,左对齐
%05d用 0 填充
%.2f小数点后保留 2 位
%8.2f宽度为 8,保留 2 位小数
%.3s最多取字符串的 3 个字符
%+d始终显示正负号
%*d宽度取自下一个参数

注意,2.5 用 %.0f 打印出来是 2:这里 Go 采用四舍六入五成双。对字符串来说,宽度按 rune 计数,而不是按显示列数,所以中日韩字符和 emoji 仍可能让表格错位。要对齐长度不一的文本列,可以交给 text/tabwriter 去计算宽度。

参数索引

%[n] 按位置选取参数,这样同一个参数就可以重复使用:

fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10)             // 10 a 1010

Errorf 与 %w

fmt.Errorf 根据格式字符串构造一个 error。配合 %w,它还会包装另一个错误,让调用方仍能识别出原错误:

调用方可能需要检查原因时用 %w,有意隐藏原因时用 %v。从 Go 1.20 开始,一次 Errorf 调用可以包含多个 %w。错误处理一页会深入讲解错误包装。

用 String() 自定义格式

任何带有 String() string 方法的类型,都能控制 %v、%s 和 Println 如何显示它:

%d 会绕过 String(),打印底层的数字。对于错误类型,对应的方法是 Error() string,它的优先级高于 String()。

动词用错时

fmt 永远不会因为格式错误而 panic。它会把问题直接打印在输出里:

fmt.Printf("%d\n", "oops")
fmt.Printf("%d %d\n", 1)
fmt.Printf("%d\n", 1, 2)
%!d(string=oops)
1 %!d(MISSING)
1
%!(EXTRA int=2)

这样的输出往往会一路带到生产环境,因为它不会让任何东西崩溃。go vet 能在构建时发现这三种问题:

./main.go:8:2: fmt.Printf format %d has arg "oops" of wrong type string
./main.go:9:2: fmt.Printf format %d reads arg #2, but call has 1 arg
./main.go:10:2: fmt.Printf call needs 1 arg but has 2 args

性能说明

fmt 把每个参数都当作 any 接收,并在运行时检查其类型(遇到结构体、切片和 map 时会退回到反射),这用于日志和输出没有问题,但在紧凑的循环里就会产生可测量的开销。转换单个数字时,strconv.Itoa 和 strconv.FormatFloat 比 Sprintf 更快。在循环中构建长字符串时,用 fmt.Fprintf(&b, ...) 写入 strings.Builder,而不是把 Sprintf 的结果拼接起来。

常见问题

Go 中 Println、Printf 和 Sprintf 有什么区别?

fmt.Println 打印各个参数,参数之间用空格分隔,末尾加换行。fmt.Printf 按格式字符串打印,不加换行。fmt.Sprintf 的格式化方式和 Printf 相同,但把结果作为字符串返回,而不是打印出来。fmt.Errorf 也一样,只是返回一个 error。

在 Go 中怎样打印带字段名的结构体?

用 %+v:fmt.Printf("%+v\n", user) 打印 {Name:Ana Age:31}。%v 只打印值,即 {Ana 31};%#v 打印包含类型的 Go 语法,即 main.User{Name:"Ana", Age:31}。

在 Go 中怎样把浮点数格式化为两位小数?

用 %.2f:fmt.Sprintf("%.2f", 3.14159) 返回 "3.14"。加上宽度可以对齐列,如 %8.2f;加上负号则左对齐,如 %-8.2f。strconv.FormatFloat(f, 'f', 2, 64) 不用格式字符串也能得到同样的结果。

fmt.Errorf 中的 %w 有什么作用?

%w 像 %v 一样格式化错误,同时还会包装它,让新错误携带原错误。之后 errors.Is 和 errors.As 就能找到被包装的错误:err := fmt.Errorf("load config: %w", os.ErrNotExist) 会让 errors.Is(err, os.ErrNotExist) 为 true。%w 只在 fmt.Errorf 中有效。

为什么我的输出里出现了 %!d(string=...)?

动词和参数类型不匹配,比如给 %d 传了一个字符串。fmt 不会 panic,而是把问题直接打印在输出里:%!d(string=oops)。缺少参数时打印 %!d(MISSING),参数多余时打印 %!(EXTRA int=2)。go vet 能在你运行程序之前发现这三种问题。

Coddy 编程语言插图

和 Coddy 一起学编程

开始