在 Go 语言中,fmt
包用于实现格式化的输入输出,其功能类似于 C 语言中的printf
和scanf
。格式化动词(format verbs)提供了多种方式来控制数据的输出格式。本文将详细讲解 Go 语言中常用的格式化动词及其用法。
Go 语言的格式化动词大致分为以下几类:
一般动词
%v
:以默认格式打印值。对于结构体,%+v
会打印字段名。%#v
:以 Go 语法表示值。%T
:以 Go 语法表示值的类型。%%
:打印一个百分号字符。
示例代码:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p := Person{"Alice", 30}
fmt.Printf("%%v 默认格式:%v\n", p)
fmt.Printf("%%+v 包含字段名:%+v\n", p)
fmt.Printf("%%#v Go 语法表示:%#v\n", p)
fmt.Printf("%%T 类型表示:%T\n", p)
fmt.Printf("%%%% 百分号:%%\n")
}
输出:
%v 默认格式:{Alice 30}
%+v 包含字段名:{Name:Alice Age:30}
%#v Go 语法表示:main.Person{Name:"Alice", Age:30}
%T 类型表示:main.Person
%% 百分号:%
布尔类型
%t
:打印布尔值true
或false
。
示例代码:
package main
import "fmt"
func main() {
fmt.Printf("%%t 布尔值:%t\n", true)
}
输出:
%t 布尔值:true
在当前版本的 Go 中,%v
和 %t
对布尔值的输出是相同的,但使用 %t
可以明确地表明你正在格式化布尔值。这样,代码的可读性更高,并且格式化意图更加明确。如果将来有新的格式化需求或规则,使用 %t
可能更容易维护和理解。
整数类型
%b
:二进制表示。%c
:对应的 Unicode 字符。%d
:十进制表示。%o
:八进制表示。%O
:带0o
前缀的八进制表示。%q
:单引号字符字面量,使用 Go 语法进行安全转义。%x
:十六进制表示,字母 a-f 使用小写。%X
:十六进制表示,字母 A-F 使用大写。%U
:Unicode 格式,类似于U+1234
。
示例代码:
package main
import "fmt"
func main() {
num := 255
fmt.Printf("%%b 二进制:%b\n", num)
fmt.Printf("%%c 字符:%c\n", num)
fmt.Printf("%%d 十进制:%d\n", num)
fmt.Printf("%%o 八进制:%o\n", num)
fmt.Printf("%%O 带前缀的八进制:%O\n", num)
fmt.Printf("%%q 单引号字符:%q\n", num)
fmt.Printf("%%x 小写十六进制:%x\n", num)
fmt.Printf("%%X 大写十六进制:%X\n", num)
fmt.Printf("%%U Unicode 格式:%U\n", num)
}
输出:
%b 二进制:11111111
%c 字符:ÿ
%d 十进制:255
%o 八进制:377
%O 带前缀的八进制:0o377
%q 单引号字符:'ÿ'
%x 小写十六进制:ff
%X 大写十六进制:FF
%U Unicode 格式:U+00FF
浮点数和复数
%b
:无小数点的科学计数法,以 2 的幂为指数,例如-123456p-78
。%e
:科学计数法,使用小写的e
,例如-1.234456e+78
。%E
:科学计数法,使用大写的E
,例如-1.234456E+78
。%f
:十进制形式,无科学计数法,例如123.456
。%F
:与%f
相同,表示十进制形式。%g
:自动选择%e
或%f
,对于较大指数使用%e
,对于其他情况使用%f
。精度会影响显示的有效数字数量。%G
:自动选择%E
或%F
,与%g
类似,但使用大写的E
和%F
。%x
:十六进制表示,使用小数点的科学计数法,例如-0x1.23abcp+20
。%X
:十六进制表示,使用大写字母,类似-0X1.23ABCP+20
。
示例代码:
package main
import "fmt"
func main() {
num := 12345.6789
complexNum := 1.234 + 5.678i
fmt.Printf("%%b 无小数点科学计数法:%b\n", num)
fmt.Printf("%%e 科学计数法(小写 e):%e\n", num)
fmt.Printf("%%E 科学计数法(大写 E):%E\n", num)
fmt.Printf("%%f 十进制形式:%f\n", num)
fmt.Printf("%%F 与 %%f 相同:%F\n", num)
fmt.Printf("%%g 自动选择:%g\n", num)
fmt.Printf("%%G 自动选择(大写):%G\n", num)
fmt.Printf("%%x 十六进制表示:%x\n", num)
fmt.Printf("%%X 十六进制表示(大写):%X\n", num)
fmt.Println("----------")
fmt.Printf("%%b 无小数点科学计数法(复数):%b\n", complexNum)
fmt.Printf("%%e 科学计数法(小写 e)(复数):%e\n", complexNum)
fmt.Printf("%%E 科学计数法(大写 E)(复数):%E\n", complexNum)
fmt.Printf("%%f 十进制形式(复数):%f\n", complexNum)
fmt.Printf("%%F 与 %%f 相同(复数):%F\n", complexNum)
fmt.Printf("%%g 自动选择(复数):%g\n", complexNum)
fmt.Printf("%%G 自动选择(大写)(复数):%G\n", complexNum)
fmt.Printf("%%x 十六进制表示(复数):%x\n", complexNum)
fmt.Printf("%%X 十六进制表示(大写)(复数):%X\n", complexNum)
}
输出:
%b 无小数点科学计数法:6787108751669409p-39
%e 科学计数法(小写 e):1.234568e+04
%E 科学计数法(大写 E):1.234568E+04
%f 十进制形式:12345.678900
%F 与 %f 相同:12345.678900
%g 自动选择:12345.6789
%G 自动选择(大写):12345.6789
%x 十六进制表示:0x1.81cd6e631f8a1p+13
%X 十六进制表示(大写):0X1.81CD6E631F8A1P+13
----------
%b 无小数点科学计数法(复数):(5557441940175192p-52+6392859671052419p-50i)
%e 科学计数法(小写 e)(复数):(1.234000e+00+5.678000e+00i)
%E 科学计数法(大写 E)(复数):(1.234000E+00+5.678000E+00i)
%f 十进制形式(复数):(1.234000+5.678000i)
%F 与 %f 相同(复数):(1.234000+5.678000i)
%g 自动选择(复数):(1.234+5.678i)
%G 自动选择(大写)(复数):(1.234+5.678i)
%x 十六进制表示(复数):(0x1.3be76c8b43958p+00+0x1.6b645a1cac083p+02i)
%X 十六进制表示(大写)(复数):(0X1.3BE76C8B43958P+00+0X1.6B645A1CAC083P+02i)
字符串和字节切片
%s
:原始字节。%q
:双引号字符串,使用 Go 语法进行安全转义。%x
:十六进制表示,每两个字节一个字符。%X
:十六进制表示,每两个字节一个字符,大写字母。
示例代码:
package main
import "fmt"
func main() {
str := "hello"
str1 := `"hello"`
data := []byte{72, 101, 108, 108, 111}
fmt.Printf("%%s 原始字节:%s\n", str)
fmt.Printf("%%q 双引号字符串:%q\n", str)
fmt.Printf("%%q 双引号字符串:%q\n", str1)
fmt.Printf("%%x 小写十六进制:%x\n", data)
fmt.Printf("%%X 大写十六进制:%X\n", data)
}
输出:
%s 原始字节:hello
%q 双引号字符串:"hello"
%q 双引号字符串:"\"hello\""
%x 小写十六进制:48656c6c6f
%X 大写十六进制:48656C6C6F
切片
对于切片,%p
用于打印第一个元素的地址,以十六进制表示,带 0x
前缀。其他格式化动词(如 %b
, %d
, %o
, %x
, %X
)在切片上没有特殊意义,主要用于处理切片内容的打印。
示例代码:
package main
import "fmt"
func main() {
slice := []int{10, 20, 30}
fmt.Printf("%%p 切片首元素地址:%p\n", &slice[0])
fmt.Printf("%%b 二进制表示(切片):%b\n", slice)
fmt.Printf("%%d 十进制表示(切片):%d\n", slice)
fmt.Printf("%%o 八进制表示(切片):%o\n", slice)
fmt.Printf("%%x 小写十六进制表示(切片):%x\n", slice)
fmt.Printf("%%X 大写十六进制表示(切片):%X\n", slice)
}
输出:
%p 切片首元素地址:0xc000010200
%b 二进制表示(切片):[00001010 00010100 00011110]
%d 十进制表示(切片):[10 20 30]
$o 八进制表示(切片):[12 24 36]
%x 小写十六进制表示(切片):[a 14 1e]
%X 大写十六进制表示(切片):[A 14 1E]
指针
对于指针,%p
用于打印指针地址,以十六进制表示,带 0x
前缀。%b
, %d
, %o
, %x
, %X
等动词也可以用来处理指针,但它们会将指针值作为整数处理。
示例代码:
package main
import "fmt"
func main() {
var num int = 42
ptr := &num
fmt.Printf("%%p 指针地址:%p\n", ptr)
fmt.Printf("%%b 二进制表示(指针):%b\n", ptr)
fmt.Printf("%%d 十进制表示(指针):%d\n", ptr)
fmt.Printf("%%o 八进制表示(指针):%o\n", ptr)
fmt.Printf("%%x 小写十六进制表示(指针):%x\n", ptr)
fmt.Printf("%%X 大写十六进制表示(指针):%X\n", ptr)
}
输出:
%p 指针地址:0xc000010200
%b 二进制表示(指针):1100000000000000000000100000000000000000000000000000000000000000
%d 十进制表示(指针):140737488355328
%o 八进制表示(指针):1000000000000
%x 小写十六进制表示(指针):c000010200
%X 大写十六进制表示(指针):C000010200
各类数据类型的 %v
默认格式
Go 语言中的 %v
格式化动词用于以默认格式打印值。以下是不同类型数据的默认格式化规则:
- 布尔值 (
bool
): 使用%t
打印true
或false
。 - 整数 (
int
,int8
等): 使用%d
打印十进制整数。如果使用%#v
,则以十六进制形式加上0x
前缀打印。 - 无符号整数 (
uint
,uint8
等): 同样使用%d
打印十进制整数,而使用%#v
时,则以十六进制形式加上0x
前缀打印。 - 浮点数 (
float32
,complex64
等): 使用%g
打印,根据需要选择科学计数法或十进制表示。 - 字符串 (
string
): 使用%s
打印原始字符串。 - 通道 (
chan
): 使用%p
打印通道的地址。 - 指针 (
pointer
): 使用%p
打印指针的地址。
对于复合对象,如结构体、数组、切片和 map,%v
会递归打印其元素,格式如下:
- 结构体 (
struct
): 以{field0 field1 ...}
的形式打印字段及其值。 - 数组、切片 (
array
,slice
): 以[elem0 elem1 ...]
的形式打印元素。 - map (
map
): 以map[key1:value1 key2:value2 ...]
的形式打印键值对。 - 指向上述类型的指针 (
pointer to above
): 以&{}
,&[]
,&map[]
的形式打印指针。
示例代码:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
b := true
i := 123
u := uint(456)
f := 789.01
s := "hello"
c := make(chan int)
p := &Person{"Alice", 30}
// Default format for each type
fmt.Printf("%%v 布尔值:%v\n", b)
fmt.Printf("%%v 整数:%v\n", i)
fmt.Printf("%%#v 十六进制整数:%#v\n", i)
fmt.Printf("%%v 无符号整数:%v\n", u)
fmt.Printf("%%#v 十六进制无符号整数:%#v\n", u)
fmt.Printf("%%v 浮点数:%v\n", f)
fmt.Printf("%%v 字符串:%v\n", s)
fmt.Printf("%%v 通道:%v\n", c)
fmt.Printf("%%v 指针:%v\n", p)
// Compound objects
arr := [3]int{1, 2, 3}
slice := []string{"a", "b", "c"}
m := map[string]int{"one": 1, "two": 2}
fmt.Printf("%%v 数组:%v\n", arr)
fmt.Printf("%%v 切片:%v\n", slice)
fmt.Printf("%%v map:%v\n", m)
fmt.Printf("%%v 数组指针:%v\n", &arr)
fmt.Printf("%%v 切片指针:%v\n", &slice)
fmt.Printf("%%v map指针:%v\n", &m)
}
输出:
%v 布尔值:true
%v 整数:123
%#v 十六进制整数:123
%v 无符号整数:456
%#v 十六进制无符号整数:0x1c8
%v 浮点数:789.01
%v 字符串:hello
%v 通道:0xc00007c070
%v 指针:&{Alice 30}
%v 数组:[1 2 3]
%v 切片:[a b c]
%v map:map[one:1 two:2]
%v 数组指针:&[1 2 3]
%v 切片指针:&[a b c]
%v map指针:&map[one:1 two:2]
指定宽度与精度
在 Go 语言中,格式化动词的宽度和精度设置用于控制输出的格式和显示。
宽度设置
宽度指的是格式化输出的最小宽度。它确保输出至少占据指定的字符位置。如果实际内容小于指定的宽度,格式化函数会在内容的左侧(默认)填充空格,直到达到指定的宽度。如果实际内容的宽度超过了指定的宽度,fmt 包的格式化动词会忽略宽度设置,直接输出实际内容的完整形式。对于数字和字符串,这个设置影响输出的对齐方式。
- 设置宽度:在格式化动词前面加上一个数字,如
%10f
。这表示输出的总宽度至少为 10 个字符。
效果示例:
package main
import "fmt"
func main() {
num := 123.45
str := "GoLang"
fmt.Printf("宽度 10 的浮点数:%10f\n", num) // 输出 123.450000 右对齐,宽度为 10
fmt.Printf("宽度 10 的字符串:%10s\n", str) // 输出 ' GoLang' 右对齐,宽度为 10
}
输出:
宽度 10 的浮点数: 123.450000
宽度 10 的字符串: GoLang
精度设置
精度指的是显示的小数点后的位数(对于浮点数)或字符数(对于字符串)。它控制了输出的详细程度。例如,对于浮点数,精度设置决定了显示多少位小数;对于字符串,精度设置限制了显示的最大字符数。
- 设置精度:在格式化动词中加上一个句点和数字,如
%.2f
。这表示浮点数的精度为 2,即小数点后显示 2 位数字。
效果示例:
package main
import "fmt"
func main() {
num := 123.456789
str := "GoLang Programming"
fmt.Printf("默认精度的浮点数:%f\n", num) // 输出 123.456789 默认精度 6
fmt.Printf("精度 2 的浮点数:%.2f\n", num) // 输出 123.46 精度为 2
fmt.Printf("精度 5 的字符串:%.5s\n", str) // 输出 'GoLan' 精度为 5,截断字符串
}
输出:
默认精度的浮点数:123.456789
精度 2 的浮点数:123.46
精度 5 的字符串:GoLan
宽度和精度的组合
你可以同时设置宽度和精度。宽度确定输出的总宽度,而精度确定内容的详细程度。例如 %9.2f
表示宽度为 9,总宽度包含小数点及两位小数。
示例代码:
package main
import "fmt"
func main() {
num := 123.456
str := "GoLang"
fmt.Printf("宽度 9,精度 2 的浮点数:%9.2f\n", num) // 输出 123.46 宽度 9,精度 2
fmt.Printf("宽度 10,精度 5 的字符串:%10.5s\n", str) // 输出 ' GoLan' 宽度 10,精度 5
}
输出:
宽度 9,精度 2 的浮点数: 123.46
宽度 10,精度 5 的字符串: GoLan
其他标志
在 Go 语言的 fmt
包中,除了基本的格式化动词外,还有一些其他的标志可以进一步控制输出的格式。这些标志包括 +
、-
、#
、
(空格)、0
等,以及 *
符号。
+
:始终打印符号
- 对于数字类型,总是打印正负号。
- 对于
%q
,保证输出 ASCII 字符串。
示例代码:
package main
import "fmt"
func main() {
num := 42
str := "hello 世界 🌍"
fmt.Printf("%%+d 始终打印符号:%+d\n", num)
fmt.Printf("%%+q 保证 ASCII 输出:%+q\n", str)
}
输出:
%+d 始终打印符号:+42
%+q 保证 ASCII 输出:"hello \u4e16\u754c \U0001f30d"
-
:左对齐字段
将字段内容左对齐,填充空间在右侧。
示例代码:
package main
import "fmt"
func main() {
num := 42
str := "hello"
fmt.Printf("%%6d 右对齐,宽度 6:%6d\n", num)
fmt.Printf("%%-6d 左对齐,宽度 6:%-6d\n", num)
fmt.Printf("%%10s 右对齐,宽度 10:%10s\n", str)
fmt.Printf("%%-10s 左对齐,宽度 10:%-10s\n", str)
}
输出:
%6d 右对齐,宽度 6: 42
%-6d 左对齐,宽度 6:42
%10s 右对齐,宽度 10: hello
%-10s 左对齐,宽度 10:hello
#
:使用特殊格式
- 为二进制、八进制和十六进制值添加前缀。
- 对于
%q
,如果[strconv.CanBackquote]
返回true
,打印原始(反引号)字符串。 - 对于
%e
、%E
、%f
、%F
、%g
和%G
,始终打印小数点。 - 对于
%g
和%G
,不移除尾随零。 - 对于
%U
,以U+0078 'x'
形式写出可打印字符。
示例代码:
package main
import "fmt"
func main() {
num := 42
str := "hello"
fmt.Printf("%%#b 二进制,前缀:%#b\n", num)
fmt.Printf("%%#o 八进制,前缀:%#o\n", num)
fmt.Printf("%%#x 十六进制,前缀:%#x\n", num)
fmt.Printf("%%#X 大写十六进制,前缀:%#X\n", num)
fmt.Printf("%%#q 原始字符串:%#q\n", str)
fmt.Printf("%%#e 科学计数法,带小数点:%#e\n", 12345.6789)
fmt.Printf("%%#g 不移除尾随零:%#g\n", 12345.6789)
fmt.Printf("%%#U Unicode 格式:%#U\n", 'x')
}
输出:
%#b 二进制,前缀:0b101010
%#o 八进制,前缀:052
%#x 十六进制,前缀:0x2a
%#X 大写十六进制,前缀:0X2A
%#q 原始字符串:`hello`
%#e 科学计数法,带小数点:1.234568e+04
%#g 不移除尾随零:12345.6789
%#U Unicode 格式:U+0078 'x'
(空格):留空空间
- 为数字之间留空格以避免省略符号。
- 在打印字节切片的十六进制表示时,添加空格分隔每两个字节。
示例代码:
package main
import "fmt"
func main() {
num1 := 42
num2 := -42
data := []byte{72, 101, 108, 108, 111}
fmt.Printf("%% d 留空空间:% d\n", num1)
fmt.Printf("%% d 留空空间:% d\n", num2)
fmt.Printf("%%x 十六进制:%x\n", data)
fmt.Printf("%% x 十六进制,分隔字节:% x\n", data)
}
输出:
% d 留空空间: 42
% d 留空空间:-42
%x 十六进制:48656c6c6f
% x 十六进制,分隔字节:48 65 6c 6c 6f
0
:用零填充
用零填充数字,移动填充在符号后面。
示例代码:
package main
import "fmt"
func main() {
num := 42
num1 := -42
fmt.Printf("%%05d 用零填充,宽度 5:%05d\n", num)
fmt.Printf("%%05d 用零填充,宽度 5:%05d\n", num1)
fmt.Printf("%%#04x 用零填充,宽度 4,带前缀:%#04x\n", num)
fmt.Printf("%%#04x 用零填充,宽度 4,带前缀:%#04x\n", num1)
}
输出:
%05d 用零填充,宽度 5:00042
%05d 用零填充,宽度 5:-0042
%#04x 用零填充,宽度 4,带前缀:0x002a
%#04x 用零填充,宽度 4,带前缀:-0x02a
*
:动态指定宽度和精度
在 Go 的 fmt 包中,一些格式化动词(如 %s
, %d
, %f
等)可以接受宽度和精度参数。这些参数通常是直接在格式化字符串中指定的,例如 %10s
表示宽度为 10
的字符串。如果你使用 *
符号,宽度或精度将通过额外的参数传递,而不是硬编码。
语法:
%*s
:宽度由后续参数提供。%.*s
:精度由后续参数提供。%*.*s
:宽度和精度都由后续参数提供。
示例代码
下面是一些示例代码,演示如何使用 *
符号动态设置格式化宽度和精度:
package main
import (
"fmt"
)
func main() {
// 使用 * 符号动态设置宽度
width := 20
str := "hello"
// 输出宽度为 20 的字符串 "hello"
fmt.Printf("|%*s|\n", width, str)
// 使用 * 符号动态设置精度
precision := 3
pi := 3.14159
// 输出精度为 3 的浮点数 3.142
fmt.Printf("|%.*f|\n", precision, pi)
// 使用 * 符号动态设置宽度和精度
width = 10
precision = 2
// 输出宽度为 10,精度为 2 的浮点数 3.14
fmt.Printf("|%*.*f|\n", width, precision, pi)
}
输出结果
| hello|
|3.142|
| 3.14|
显式参数索引
在 Go 的格式化函数中(比如 fmt.Printf
、fmt.Sprintf
和 fmt.Fprintf
),你可以用 [n]
来告诉函数使用第 n
个参数来进行格式化,使用 [n]
后,后面的格式化动词会依次使用下一个参数,除非再次指定。你可以用这种方式灵活地控制输出的格式,特别是当你需要重复使用参数或者改变参数的显示方式时。
例子 1
package main
import (
"fmt"
)
func main() {
// 使用索引进行参数复用
fmt.Printf("%[1]s 和 %[1]s 的朋友\n", "Tom")
// 输出 "Tom 和 Tom 的朋友"
fmt.Printf("%[2]d 是大于 %[1]d 的数字\n", 11, 22)
// 输出 "22 是大于 11 的数字"
// 展示使用 [n] 后,后面的动词自动递增
fmt.Printf("%d, %[3]d, %[1]d, %d, %d, %[1]d, %d\n", 100, 200, 300)
// 输出 "100, 300, 100, 200, 300, 100, 200"
}
这个示例展示了索引取值的几种用法:
- 使用
[n]
语法来复用同一个参数。 - 控制参数的选择和格式化方式。
- 在格式化字符串中,使用
[n]
语法后,后续的动词会自动递增,除非你再次指定索引。
例子 2
package main
import (
"fmt"
)
func main() {
fmt.Printf("%[3]*.[2]*[1]f\n", 12.0, 2, 6)
// 输出 " 12.00"
}
%[3]*.[2]*[1]f
的解释:
[3]*
表示宽度由第三个参数(6)决定。.[2]*
表示精度由第二个参数(2)决定。[1]f
表示使用第一个参数(12.0)来格式化。
所以,输出是 " 12.00",这里的宽度是 6,精度是 2。
额外的格式化规则和接口处理
在 Go 语言的 fmt
包中,除了基本的格式化动词外,还有一些额外的规则和接口处理机制,这些机制会影响最终的输出结果。以下是这些规则的详细讲解和示例:
Print、Println 和 Printf
fmt.Print
:相当于对每个操作数使用%v
格式化,且不会添加任何额外格式。fmt.Println
:在每个操作数之间插入空格,并在末尾添加换行符。fmt.Printf
:允许使用格式化动词对操作数进行自定义格式化。
示例代码:
package main
import "fmt"
func main() {
i := 23
fmt.Print("Print 默认格式:")
fmt.Print(i)
fmt.Print("\n")
fmt.Println("Println 默认格式:", i)
fmt.Printf("Printf 默认格式:%v\n", i)
}
输出:
Print 默认格式:23
Println 默认格式: 23
Printf 默认格式:23
接口处理
对于实现了特定接口的操作数,fmt
包会根据这些接口的实现来决定如何格式化:
reflect.Value
:如果操作数是reflect.Value
类型,fmt
包会用其持有的具体值替换reflect.Value
对象。Formatter
接口:如果操作数实现了Formatter
接口(包含Format
方法),fmt
包会调用该方法来控制格式化。GoStringer
接口:如果%#v
格式化动词用于操作数,并且该操作数实现了GoStringer
接口(包含GoString
方法),fmt
包会调用该方法。
示例代码:
package main
import (
"fmt"
"reflect"
)
type CustomFormatter struct {
Value int
}
func (cf CustomFormatter) Format(f fmt.State, c rune) {
fmt.Fprintf(f, "[%d]", cf.Value)
}
type GoStringerExample struct {
Value int
}
func (gse GoStringerExample) GoString() string {
return fmt.Sprintf("<GoString: %d>", gse.Value)
}
func main() {
// reflect.Value
v := reflect.ValueOf(42)
fmt.Printf("reflect.Value 格式:%v\n", v)
// Formatter 接口
cf := CustomFormatter{Value: 100}
fmt.Printf("Formatter 接口格式:%v\n", cf)
// GoStringer 接口
gse := GoStringerExample{Value: 200}
fmt.Printf("GoStringer 接口格式:%#v\n", gse)
}
输出:
reflect.Value 格式:42
Formatter 接口格式:[100]
GoStringer 接口格式:<GoString: 200>
错误和字符串接口处理
error
接口:如果操作数实现了error
接口,fmt
包会调用Error
方法来将对象转换为字符串。String()
方法:如果操作数实现了String() string
方法,fmt
包会调用该方法将对象转换为字符串。
示例代码:
package main
import "fmt"
type MyError struct {
Message string
}
func (e MyError) Error() string {
return "MyError: " + e.Message
}
type StringerExample struct {
Value string
}
func (se StringerExample) String() string {
return "Stringer: " + se.Value
}
func main() {
// 实现了 error 接口
err := MyError{Message: "something went wrong"}
fmt.Printf("error 接口格式:%v\n", err)
// 实现了 String() 方法
se := StringerExample{Value: "example"}
fmt.Printf("String() 方法格式:%v\n", se)
}
输出:
error 接口格式:MyError: something went wrong
String() 方法格式:Stringer: example
复合操作数的格式化
对于切片和结构体等复合操作数,格式化动词会递归地应用于其元素。例如,%q
会对每个字符串元素进行引用,而 %6.2f
会对每个浮点数元素进行格式化。
示例代码:
package main
import "fmt"
func main() {
strings := []string{"hello", "world"}
floats := []float64{12.345, 67.890}
fmt.Printf("%%q 格式化切片:%q\n", strings)
fmt.Printf("%%6.2f 格式化浮点数切片:%6.2f %6.2f\n", floats[0], floats[1])
}
输出:
%q 格式化切片:"hello" "world"
%6.2f 格式化浮点数切片: 12.35 67.89
避免递归
在自定义类型的 String()
方法中,为了避免递归调用,可以将值转换为基本类型后再进行格式化。
示例代码:
package main
import "fmt"
type X string
func (x X) String() string {
return fmt.Sprintf("<%s>", string(x))
}
func main() {
x := X("example")
fmt.Printf("避免递归格式化:%v\n", x)
}
输出:
避免递归格式化:<example>
结语
了解这些格式化动词可以帮助你更灵活地控制 Go 程序中的输出格式。无论是打印基本数据类型还是复杂的数据结构,掌握这些动词和标志能够提高代码的可读性和调试效率。希望本文的详细讲解和示例能够帮助你更好地使用 fmt
包进行格式化输出。