go pkg fmt
Go 语言中的 fmt
包是标准库中用于 格式化输入输出 的核心工具,其名称源自“format”的缩写。通过 格式化动词 和 灵活的函数设计 ,兼顾了简单使用与深度定制需求。无论是基础的终端交互,还是复杂的文件读写和字符串处理,均可通过该包高效实现。对于需要精细化控制的场景,结合自定义接口(如 Stringer
、Formatter
)能进一步提升代码的可读性和维护性
一、通用格式动词
动词 | 说明 | 示例(输入 → 输出) |
---|
%v | 默认格式输出值。对结构体,%+v 会添加字段名;%#v 输出 Go 语法表示形式。 | fmt.Printf("%v", 3.14) → 3.14 |
%#v | 输出值的 Go 语法表示(包括类型信息)。浮点数的无穷大和 NaN 显示为 ±Inf 和 NaN 。 | fmt.Printf("%#v", "hello") → "hello" |
%T | 输出值的类型(Go 语法格式)。 | fmt.Printf("%T", 42) → int |
%% | 输出百分号 % ,不消耗任何参数。 | fmt.Printf("100%%") → 100% |
二、布尔类型
动词 | 说明 | 示例 |
---|
%t | 输出 true 或 false 。 | fmt.Printf("%t", true) → true |
三、整数类型
动词 | 说明 | 示例(输入 → 输出) |
---|
%b | 二进制格式。 | fmt.Printf("%b", 5) → 101 |
%c | Unicode 码点对应的字符。 | fmt.Printf("%c", 65) → A |
%d | 十进制格式。 | fmt.Printf("%d", 0x1F) → 31 |
%o | 八进制格式。%#O 添加 0o 前缀。 | fmt.Printf("%O", 15) → 0o17 |
%x | 十六进制(小写字母 a-f)。%#x 添加 0x 前缀。 | fmt.Printf("%x", 255) → ff |
%X | 十六进制(大写字母 A-F)。%#X 添加 0X 前缀。 | fmt.Printf("%X", 255) → FF |
%q | 单引号包裹的字符(支持转义不可打印字符)。 | fmt.Printf("%q", 'A') → 'A' |
%U | Unicode 格式:U+1234 ,等价于 U+%04X 。%#U 添加字符和引号。 | fmt.Printf("%U", 'A') → U+0041 |
四、浮点数与复数
动词 | 说明 | 示例(输入 → 输出) |
---|
%b | 科学计数法(指数为 2 的幂),如 -123456p-78 。 | fmt.Printf("%b", 3.14) → 707065141497117p-48 |
%e | 科学计数法(小写 e)。 | fmt.Printf("%e", 1234.5678) → 1.234568e+03 |
%E | 科学计数法(大写 E)。 | fmt.Printf("%E", 1234.5678) → 1.234568E+03 |
%f | 十进制无指数格式。 | fmt.Printf("%f", 3.14) → 3.140000 |
%g | 自动选择 %e 或 %f (根据指数大小)。精度表示最大有效数字。 | fmt.Printf("%.3g", 12.345) → 12.3 |
%G | 类似 %g ,但使用大写 E。 | fmt.Printf("%G", 1234.5678) → 1.234568E+03 |
%x | 十六进制浮点数(小写)。 | fmt.Printf("%x", 3.14) → 0x1.91eb851eb851fp+1 |
%X | 十六进制浮点数(大写)。 | fmt.Printf("%X", 3.14) → 0X1.91EB851EB851FP+1 |
五、字符串与字节切片
动词 | 说明 | 示例(输入 → 输出) |
---|
%s | 原始字节输出(不转义)。 | fmt.Printf("%s", "A\tB") → A B |
%q | 双引号包裹的字符串(支持转义特殊字符)。%+q 强制 ASCII 输出。 | fmt.Printf("%q", "A\tB") → "A\tB" |
%x | 十六进制(小写,每字节两个字符)。% x 用空格分隔字节。 | fmt.Printf("%x", "Go") → 476f |
%X | 十六进制(大写,每字节两个字符)。% X 用空格分隔字节。 | fmt.Printf("%X", "Go") → 476F |
六、指针与切片
动词 | 说明 | 示例(输入 → 输出) |
---|
%p | 指针地址(十六进制,带 0x 前缀)。%#p 省略 0x 。 | a := 42; fmt.Printf("%p", &a) → 0xc0000140a8 |
切片 | 默认 %v 输出 [elem1 elem2 ...] ,%+v 无特殊,%#v 带类型信息。 | fmt.Printf("%v", []int{1,2}) → [1 2] |
七、宽度与精度控制
语法
1
| %[flags][width][.precision]verb
|
规则
- 宽度:最小输出字符数(不足补空格/0)。
- 精度:
- 浮点数:小数点后位数(
%g
/%G
为最大有效位数)。 - 字符串:最大字符数(按 rune 截断,十六进制按字节截断)。
- 标志:
+
:显示数值的正负号(如 %+d
→ +42
)。-
:左对齐(默认右对齐)。0
:用 0
填充(如 %05d
→ 00042
)。#
:改变格式(如 %#x
→ 0x1a
)。
(空格):正数留空(如 % d
→ 42
)。
示例
1
2
3
| fmt.Printf("%6.2f", 3.1415) // 输出 " 3.14"(宽度6,精度2)
fmt.Printf("%-8s", "Go") // 输出 "Go "(左对齐)
fmt.Printf("%+05d", 42) // 输出 "+0042"(0填充,显示符号)
|
八、特殊类型处理
结构体
%v
格式:{field1 value1 field2 value2 ...}
。%+v
格式:添加字段名(如 {Name:"Alice", Age:30}
)。%#v
格式:Go 语法表示(如 main.Person{Name:"Alice", Age:30}
)。
Map
- 默认格式:
map[key1:value1 key2:value2 ...]
。 %#v
格式:map[KeyType]ValueType{key1:value1, ...}
。
接口值
- 输出内部具体值,而非接口本身(如
var i interface{} = 42
→ 42
)。
九、优先级与递归处理
- 接口处理优先级:
- 若值实现
Formatter
接口,调用其 Format
方法。 - 若使用
%#v
且实现 GoStringer
接口,调用 GoString()
。 - 若实现
error
或 String() string
接口,调用对应方法。
- 递归陷阱
十、Print 系列函数对比
函数 | 行为 |
---|
Print | 无格式,等价于对每个参数用 %v 。 |
Println | 自动添加空格分隔符和换行符。 |
Printf | 按指定格式输出。 |
Sprint | 返回格式化后的字符串,不输出到控制台。 |
十一、注意事项
- 未导出字段:结构体的未导出字段不会触发
String()
或 Error()
方法。 - 浮点数精度:
%g
会删除末尾的零(如 3.140000
→ 3.14
)。 - 切片与字符串:字节切片(
[]byte
)使用 %s
/%q
时按字符串处理。 - 指针与整数:
%d
、%x
等动词可格式化指针地址为整数。
一、基本语法
在 Printf
、Sprintf
、Fprintf
等函数中,默认情况下格式化动词按参数顺序依次处理。但通过 [n]
语法,可以显式指定参数的索引(从 1
开始计数)。
格式规则
索引
:必须是 [n]
形式,n
为参数位置(从 1 开始)。- 若对
宽度
或 精度
使用 *
,可用 [n]
指定参数来源。
二、核心用法
1. 交换参数顺序
1
| fmt.Sprintf("%[2]d %[1]d", 11, 22) // 输出 "22 11"
|
%[2]d
:使用第 2 个参数 22
。%[1]d
:使用第 1 个参数 11
。
2. 动态设置宽度/精度
1
2
3
| fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6)
// 等价于 fmt.Sprintf("%6.2f", 12.0)
// 输出 " 12.00"
|
%[3]*
:宽度取第 3 个参数 6
。.[2]*
:精度取第 2 个参数 2
。[1]f
:值取第 1 个参数 12.0
。
3. 重复使用参数
1
2
| fmt.Sprintf("%d %d %#[1]x %#x", 16, 17)
// 输出 "16 17 0x10 0x11"
|
- 前两个
%d
按顺序使用参数 16
和 17
。 %#[1]x
:再次使用第 1 个参数 16
,格式化为十六进制并添加 0x
。%#x
:继续使用下一个参数(即第 2 个参数 17
),格式化为十六进制。
三、索引对后续动词的影响
显式索引会重置后续动词的参数位置:
1
2
| fmt.Printf("%[2]s %s %[1]s", "A", "B", "C")
// 输出 "B C A"
|
%[2]s
→ 参数 2(“B”)。- 下一个
%s
使用参数 3(“C”)。 %[1]s
→ 参数 1(“A”)。
四、复杂示例
动态格式控制
1
2
3
4
5
6
7
| width := 8
precision := 3
value := 3.1415926
fmt.Printf("%[3]*.[2]*[1]f", value, precision, width)
// 等价于 fmt.Printf("%8.3f", value)
// 输出 " 3.142"
|
%[3]*
:宽度来自第 3 个参数 width=8
。.[2]*
:精度来自第 2 个参数 precision=3
。[1]f
:值来自第 1 个参数 value=3.1415926
。
多次复用同一参数
1
2
3
| name := "Alice"
fmt.Printf("%[1]s: %[1]q (length=%d)", name, len(name))
// 输出 "Alice: "Alice" (length=5)"
|
%[1]s
和 %[1]q
均使用第 1 个参数 name
。%d
使用第 2 个参数 len(name)
。
五、注意事项
- 索引范围:索引从
1
开始,且不能超过参数总数。 - 动态参数顺序:显式索引后,后续动词默认从
n+1
开始。 - 可读性:复杂索引可能降低代码可读性,建议适度使用。
- 错误示例:
1
2
| fmt.Printf("%[0]d", 42) // 错误:索引从 1 开始
fmt.Printf("%[3]d", 1, 2) // 错误:索引 3 超出参数范围
|
六、使用场景
场景 | 示例 | 说明 |
---|
交换输出顺序 | %[2]d %[1]d | 灵活控制参数顺序 |
重复使用参数 | %d %#[1]x | 同一值不同格式输出 |
动态设置格式参数 | %[3]*.[2]*[1]f | 宽度/精度来自变量 |
国际化输出 | "%[2]s: %[1]d" (不同语言顺序不同) | 适应不同语言的参数顺序需求 |
通过显式参数索引,可以实现更灵活的格式化控制,尤其在需要重复使用参数或动态设置格式时非常有用。
一、错误类型与格式
当格式化动词与参数类型不匹配或参数数量不匹配时,fmt
包会生成特定错误信息,格式为 %!动词(错误描述)
。
1. 类型不匹配或未知动词
场景 | 示例代码 | 输出结果 | 说明 |
---|
参数类型与动词不兼容 | Printf("%d", "hi") | %!d(string=hi) | %d 需要整数,但传入字符串。 |
使用不存在的动词 | Printf("%k", 42) | %!k(int=42) | %k 是未定义的格式化动词。 |
2. 参数数量问题
场景 | 示例代码 | 输出结果 | 说明 |
---|
参数过多 | Printf("hi", "guys") | hi%!(EXTRA string=guys) | 格式字符串无占位符,多余参数标记为 EXTRA 。 |
参数不足 | Printf("hi%d") | hi%!d(MISSING) | %d 无对应参数。 |
3. 动态宽度/精度参数错误
场景 | 示例代码 | 输出结果 | 说明 |
---|
动态宽度参数非整数 | Printf("%*s", 4.5, "hi") | %!(BADWIDTH)hi | * 需要整数,但传入浮点数 4.5 。 |
动态精度参数非整数 | Printf("%.*s", 4.5, "hi") | %!(BADPREC)hi | .* 需要整数,但传入浮点数 4.5 。 |
4. 参数索引错误
场景 | 示例代码 | 输出结果 | 说明 |
---|
无效的索引引用 | Printf("%*[2]d", 7) | %!d(BADINDEX) | 索引 [2] 超出参数范围(仅 1 个参数)。 |
索引语法错误 | Printf("%.[2]d", 7) | %!d(BADINDEX) | . 后缺少 * 或数值。 |
二、Panic 处理
如果 Error()
、String()
或 GoString()
方法触发 panic,错误信息会被捕获并标记为 PANIC
。
示例
1
2
3
4
5
6
7
| type MyType struct{}
func (m *MyType) String() string { panic("bad") }
func main() {
var m MyType
fmt.Printf("%s", m) // 输出:%!s(PANIC=bad)
}
|
特殊场景
场景 | 输出结果 | 说明 |
---|
因 nil 接收者触发 panic | <nil> | 直接输出 <nil> ,无错误修饰。 |
三、错误格式总结
所有错误信息遵循以下模式:
- 动词:触发错误的格式化动词(如
%d
对应 d
)。 - 错误类型:错误类别(如
BADWIDTH
、MISSING
)。 - 描述:具体错误原因(如
string=hi
表示传入字符串)。
四、调试建议
- 检查动词与参数类型:确保
%d
对应整数,%s
对应字符串等。 - 验证参数数量:格式化字符串中的动词数量需与参数数量匹配。
- 动态参数校验:确保
*
宽度/精度参数为整数。 - 处理
nil
值:为可能为 nil
的类型实现安全的 String()
方法。
五、错误示例解析
示例 1
1
| fmt.Printf("%d %s", "hello", 42)
|
- 输出:
%!d(string=hello) %!s(int=42)
- 原因:
%d
需要整数但传入字符串,%s
需要字符串但传入整数。
示例 2
1
| fmt.Printf("%[3]d", 1, 2)
|
- 输出:
%!d(BADINDEX)
- 原因:索引
[3]
超出参数范围(只有 2 个参数)。
通过理解这些错误信息,可以快速定位格式化代码中的问题,尤其是在处理复杂格式字符串或动态参数时。
一、扫描函数分类
函数组 | 输入源 | 行为 |
---|
Scan | os.Stdin | 将换行符视为空格,持续读取直到所有参数匹配。 |
Scanln | os.Stdin | 遇到换行符停止扫描,参数必须后跟换行符或 EOF。 |
Scanf | os.Stdin | 按格式化字符串解析输入(类似 Printf )。 |
Fscan | io.Reader | 类似 Scan ,但可指定任意 io.Reader (如文件、网络连接)。 |
Fscanln | io.Reader | 类似 Scanln ,但可指定 io.Reader 。 |
Fscanf | io.Reader | 类似 Scanf ,但可指定 io.Reader 。 |
Sscan | 字符串 | 从字符串中读取数据,行为类似 Scan 。 |
Sscanln | 字符串 | 从字符串中读取数据,行为类似 Scanln 。 |
Sscanf | 字符串 | 从字符串中按格式化字符串解析数据,行为类似 Scanf 。 |
二、输入处理规则
1. 空格与换行符
- Scan/Fscan/Sscan:将换行符(
\n
)视为普通空格。 - Scanln/Fscanln/Sscanln:遇到换行符后停止扫描,且要求参数后必须紧跟换行符或 EOF。
- Scanf/Fscanf/Sscanf:根据格式字符串处理空格和换行符。
2. 格式字符串中的空格
- 普通字符:必须与输入严格匹配(如
"a%b"
要求输入中有 a
后跟 %
匹配的内容)。 - 换行符:格式中的
\n
匹配输入中的零或多个空格后跟一个换行符。 - 连续空格:格式中的连续空格匹配输入中的任意多个空格(至少一个,除非在换行符旁)。
三、格式化动词
通用动词
动词 | 说明 | 示例输入 → 解析结果 |
---|
%v | 解析默认格式的值(支持类型推断)。 | "123" → int(123) |
%t | 解析布尔值(true /false )。 | "true" → bool(true) |
%d | 解析十进制整数。支持 0b (二进制)、0o (八进制)、0x (十六进制)。 | "0x1F" → int(31) |
%x | 解析十六进制整数(不区分大小写)。 | "1a" → int(26) |
%s | 解析字符串(遇到空格或换行停止)。 | "hello world" → "hello" |
%c | 解析单个 Unicode 字符(不跳过空格)。 | "A" → rune('A') |
%f | 解析浮点数(支持科学计数法、十六进制及下划线分隔)。 | "3.14_15" → float64(3.1415) |
浮点数与复数
- 支持动词:
%b
、%e
、%E
、%f
、%F、
%g、
%G、
%x、
%X、
%v`。 - 示例:
1
2
| var f float64
fmt.Sscanf("0x1.8p+2", "%v", &f) // f = 6.0
|
特殊说明
- 不支持的 Printf 特性:
%p
、%T
、#
和 +
标志。 - 隐式空格分隔:除
%c
外,所有动词解析前会跳过前导空格。
四、宽度控制
语法
- 宽度:指定最大读取字符数(按 rune 计数,非字节)。
- 示例:
1
2
| var s string
fmt.Sscanf("1234567", "%5s", &s) // s = "12345"
|
五、前缀与下划线
- 整数:支持
0b
、0o
、0x
前缀及数字下划线分隔。1
2
| var i int
fmt.Sscanf("0b1010_1100", "%v", &i) // i = 172
|
- 浮点数:支持十六进制(如
0x1.2p3
)和下划线分隔。1
2
| var f float64
fmt.Sscanf("3_141.592_6535", "%f", &f) // f = 3141.5926535
|
六、Scanner 接口
若类型实现 Scan
方法(即实现 Scanner
接口),扫描函数将调用该方法自定义解析逻辑。
示例
1
2
3
4
5
6
7
8
9
10
11
12
| type MyType struct{ Value int }
func (m *MyType) Scan(f fmt.ScanState, verb rune) error {
s, _ := f.Token(true, nil) // 读取整个 token
m.Value, _ = strconv.Atoi(string(s))
return nil
}
func main() {
var m MyType
fmt.Sscanf("42", "%v", &m) // m.Value = 42
}
|
七、注意事项
必须传递指针:
1
2
3
| var i int
fmt.Scan(&i) // 正确
fmt.Scan(i) // 错误:非指针
|
输入未完全消费:
1
2
| var a, b int
fmt.Sscanf("10 20 30", "%d %d", &a, &b) // a=10, b=20,剩余 "30" 被忽略
|
换行符处理:
潜在数据丢失:
- 若
io.Reader
未实现 ReadRune
和 UnreadRune
,连续扫描可能跳过字符。建议使用 bufio.NewReader
包装。
八、错误示例
示例 1:参数不足
1
2
3
| var i int
_, err := fmt.Sscanf("hello", "%d", &i)
fmt.Println(err) // 输出: expected integer
|
示例 2:类型不匹配
1
2
3
| var f float64
_, err := fmt.Sscanf("not_a_float", "%f", &f)
fmt.Println(err) // 输出: parsing "not_a_float": invalid syntax
|
九、高级用法
批量扫描
1
2
3
4
5
6
| input := "John 30 175.5"
var name string
var age int
var height float64
fmt.Sscanf(input, "%s %d %f", &name, &age, &height)
// name="John", age=30, height=175.5
|
动态宽度扫描
1
2
| var s1, s2 string
fmt.Sscanf("ABCDEFG", "%3s%2s", &s1, &s2) // s1="ABC", s2="DE"
|
Functions
总结
格式化输出函数
标准输出函数
函数名 | 签名 | 说明 |
---|
Print | func Print(a ...any) (n int, err error) | 将参数格式化并写入标准输出,不添加空格,返回写入的字节数和错误 |
Printf | func Printf(format string, a ...any) (n int, err error) | 根据格式字符串格式化参数并写入标准输出,返回写入的字节数和错误 |
Println | func Println(a ...any) (n int, err error) | 将参数格式化并写入标准输出,参数间添加空格,末尾添加换行符 |
字符串构建函数
函数名 | 签名 | 说明 |
---|
Sprint | func Sprint(a ...any) string | 将参数格式化为字符串并返回,不添加空格 |
Sprintf | func Sprintf(format string, a ...any) string | 根据格式字符串格式化参数并返回字符串 |
Sprintln | func Sprintln(a ...any) string | 将参数格式化为字符串并返回,参数间添加空格,末尾添加换行符 |
文件/Writer 输出函数
函数名 | 签名 | 说明 |
---|
Fprint | func Fprint(w io.Writer, a ...any) (n int, err error) | 将参数格式化并写入指定的 io.Writer,不添加空格 |
Fprintf | func Fprintf(w io.Writer, format string, a ...any) (n int, err error) | 根据格式字符串格式化参数并写入指定的 io.Writer |
Fprintln | func Fprintln(w io.Writer, a ...any) (n int, err error) | 将参数格式化并写入指定的 io.Writer,参数间添加空格,末尾添加换行符 |
字节追加函数
函数名 | 签名 | 说明 |
---|
Append | func Append(b []byte, a ...any) []byte | 将格式化后的参数追加到字节切片中并返回 |
Appendf | func Appendf(b []byte, format string, a ...any) []byte | 根据格式字符串将格式化后的参数追加到字节切片中并返回 |
Appendln | func Appendln(b []byte, a ...any) []byte | 将格式化后的参数追加到字节切片中,参数间添加空格,末尾添加换行符 |
输入扫描函数
标准输入函数
函数名 | 签名 | 说明 |
---|
Scan | func Scan(a ...any) (n int, err error) | 从标准输入扫描文本,赋值给参数指针 |
Scanf | func Scanf(format string, a ...any) (n int, err error) | 根据格式字符串从标准输入扫描文本,赋值给参数指针 |
Scanln | func Scanln(a ...any) (n int, err error) | 从标准输入扫描文本直到换行,赋值给参数指针 |
字符串扫描函数
函数名 | 签名 | 说明 |
---|
Sscan | func Sscan(str string, a ...any) (n int, err error) | 从字符串扫描文本,赋值给参数指针 |
Sscanf | func Sscanf(str string, format string, a ...any) (n int, err error) | 根据格式字符串从字符串扫描文本,赋值给参数指针 |
Sscanln | func Sscanln(str string, a ...any) (n int, err error) | 从字符串扫描文本直到换行,赋值给参数指针 |
文件/Reader 扫描函数
函数名 | 签名 | 说明 |
---|
Fscan | func Fscan(r io.Reader, a ...any) (n int, err error) | 从指定的 io.Reader 扫描文本,赋值给参数指针 |
Fscanf | func Fscanf(r io.Reader, format string, a ...any) (n int, err error) | 根据格式字符串从指定的 io.Reader 扫描文本,赋值给参数指针 |
Fscanln | func Fscanln(r io.Reader, a ...any) (n int, err error) | 从指定的 io.Reader 扫描文本直到换行,赋值给参数指针 |
工具函数
函数名 | 签名 | 说明 |
---|
Errorf | func Errorf(format string, a ...any) error | 根据格式字符串创建格式化的错误信息 |
FormatString | func FormatString(state State, verb rune) string | 返回格式化状态的字符串表示 |
接口和类型
接口名 | 说明 |
---|
Formatter | 自定义格式化的接口,实现该接口的类型可以自定义 fmt 包的格式化行为 |
GoStringer | 定义了 GoString() 方法的接口,用于产生适合 Go 语法表示的字符串 |
ScanState | 提供扫描功能的接口,用于自定义 Scanner 的实现 |
Scanner | 自定义扫描的接口,实现该接口的类型可以自定义 fmt 包的扫描行为 |
State | 提供格式化状态的接口,用于自定义 Formatter 的实现 |
Stringer | 定义了 String() 方法的接口,用于将对象转换为字符串表示 |
这些函数和接口构成了 Go 语言中用于格式化输入输出的强大工具集,为程序提供了灵活的文本处理能力。
demo 展示
实现 Stringer 和 GoStringer

1
2
| persion: Name: John, Age: 30
persion: Persion{Name: "John", Age: 30}
|

1
2
3
4
5
| 默认: Host: localhost, Port: 5432, User: user, DB: example_db
详细信息: DBConfig{Host: localhost, Port: 5432, Username: user, Password: password, Database: example_db}
字符串表示: localhost:5432/example_db
带引号: "localhost:5432/example_db"
其他格式: unsupported format: x
|
Go fmt 包中的三个格式化接口及其优先级
在 Go 的 fmt
包中,Formatter
、Stringer
和 GoStringer
这三个接口用于控制值的格式化输出。它们各有特定用途,并按照一定的优先级顺序被调用。
优先级顺序(从高到低)
- Formatter 接口 (最高优先级)
- GoStringer 接口 (中间优先级,特定于
%#v
格式) - Stringer 接口 (最低优先级)
1
2
3
| type Formatter interface {
Format(f State, verb rune)
}
|
- 最高优先级:如果一个类型实现了此接口,
fmt
包将优先使用它来处理所有格式化操作 - 完全控制:可以自定义处理所有格式动词和标志
- 灵活性:能够根据不同的格式动词(
v
, s
, q
等)和标志(+
, #
等)提供不同输出 - 应用场景:需要对不同格式动词提供完全不同的输出形式时
2. GoStringer 接口
1
2
3
| type GoStringer interface {
GoString() string
}
|
- 特定优先级:仅当使用
%#v
格式动词且类型未实现 Formatter
接口时生效 - 用途:提供符合 Go 语法的值表示,主要用于调试和开发
- 典型输出:类似于可用于重建对象的 Go 代码片段
- 应用场景:调试时想查看值的 Go 语法表示
3. Stringer 接口
1
2
3
| type Stringer interface {
String() string
}
|
- 基础优先级:当类型未实现
Formatter
接口(对于 %v
、%s
等),且未使用 %#v
(或实现了但未使用 GoStringer)时生效 - 用途:提供类型的"原生"字符串表示
- 简单直接:只需返回一个字符串,不需要处理不同的格式动词
- 应用场景:希望类型有一个默认的字符串表示时
调用顺序逻辑
当调用 fmt
包的函数(如 Printf
, Sprintf
等)时:
- 首先检查值是否实现了
Formatter
接口,如果是,则调用其 Format 方法 - 如果没有实现
Formatter
,但格式是 %#v
且实现了 GoStringer
,则调用 GoString()
- 如果前两者都不适用,但实现了
Stringer
,则在适当的格式中使用 String()
- 如果都没实现,则使用
fmt
包的默认格式化规则