目录
格式化字符串由于字符串通常是有书面文本组成的,因此在许多情况下,我们可能希望更好地控制字符串的外观,以便通过标点,换行和缩进使字符串对人类更具可读性。对于一些特殊的使用场景,我们希望保存在程序中的变量值插入到字符串中,这就需要格式化字符串。Go对字符串格式化提供了良好的支持,在 Go 语言中,fmt.Sprintf,fmt.Printf,fmt.Fprintf, Log.Printf, log.Panicf 等函数常常会用到格式化字符串参数。格式化字符串本质上与普通的字符串无异,它是由普通的字符串加上格式化样式和参数组成的。下面我们就来谈谈Go语言的格式化样式。
格式化样式格式化样式是一种字符串形式,格式化字符以%开头,加上一个表示格式化字符类型的字符串组成了格式化样式。例如, %s表示字符串格式,%d表示整数格式。详细的格式化样式表总结如下:
格式化样式 | 描述 |
---|---|
%s | 输出字符串(string和[]byte)值 |
%d | 输出十进制整数值 |
%b | 输出整型的二进制表示形式 |
%#b | 输出整型的二进制完整表示形式 |
%o | 输出整型的八进制表示形式 |
%#o | 输出整型的八进制完整表示形式 |
%x | 输出整型的十六进制表示形式 |
%#x | 输出整型的十六进制完整表示形式 |
%X | 输出整型的十六进制表示形式(大写) |
%#X | 输出整型的十六进制完整表示形式(大写) |
%v | 输出值的本来形式 |
%+v | 输出值的本来形式,如果值为结构体,对结构体字段名和值展开 |
%#v | 输出Go语法格式的值 |
%t | 输出布尔值 |
%T | 输出值对应的Go语言类型 |
%% | 输出百分号(%) |
%c | 输出Unicode码对应的字符 |
%U | 将Unicode码转换为对应的Unicode码点 |
%f | 输出浮点数 |
%e | 输出数值的科学计数法形式 |
%E | 与%e功能相同 |
%g | 输出紧凑的浮点数 |
%p | 输出指针 |
%q | 格式化字符串,在字符串的两端加上双引号 |
%s
使用格式
%[-][length]s
- | 可选,表示左对齐 |
length | 可选,一个大于0的整数,表示格式化宽度 |
格式化字符串
%s输出字符串。%s可以将string和与string具有相同底层类型的类型输出。
package main
import "fmt"
func main() {
sentence := "I love Go."
fmt.Printf("He say: '%s'\n", sentence)
// output: He say: 'I love Go.'
type S string
fmt.Printf("He say: '%s'\n", S(sentence))
// output: He say: 'I love Go.'
}
格式化字节数组
%s也可以输出字节切片[]byte和字节数组[...]byte。
package main
import "fmt"
func main() {
list := []byte{97, 98, 99}
fmt.Printf("%s\n", list)
// output: abc
array := [4]byte{96, 97, 98, 99}
fmt.Printf("%s\n", array)
// output: `abc
}
自定义样式
通过实现fmt.Stringer接口来定制类型的%s输出。下面的代码定义了一个自定义类型MyInteger,并实现了fmt.Stringer接口,返回一个字符串"funny"。因此无论该类型变量中实际存储的值是什么,%s得到的格式化字符串永远都为"funny",即fmt.Stringer接口的返回值。
package main
import "fmt"
type MyInteger int
func (i MyInteger) String() string {
return "funny"
}
func main() {
var demo1 MyInteger = 23
fmt.Printf("%s\n", demo1)
// output: funny
var demo2 MyInteger = 298
fmt.Printf("%s\n", demo2)
// output: funny
}
%d
使用格式
%[+][-][length]d
+ | 可选,表示右对齐。默认情况下为右对齐 |
- | 可选,表示左对齐 |
length | 可选,一个大于0的整数,表示格式化宽度 |
格式化整型
%d输出整数类型。%d可以将int, int32, int64, uint, uint8, uint16, uint32, uint64和与它们底层类型相同的类型输出。
package main
import "fmt"
func main() {
var number int = 0
fmt.Printf("The value of number is: %d.\n", number)
// output: The value of number is: 0.
var number32 int32 = 1
fmt.Printf("The value of number32 is: %d.\n", number32)
// output: The value of number32 is: 1.
var number64 int64 = 2
fmt.Printf("The value of number64 is: %d.\n", number64)
// output: The value of number64 is: 2.
var numberu uint = 3
fmt.Printf("The value of numberu is: %d.\n", numberu)
// output: The value of numberu is: 3.
var numberu8 uint8 = 4
fmt.Printf("The value of numberu8 is: %d.\n", numberu8)
// output: The value of numberu8 is: 4.
var numberu16 uint16 = 5
fmt.Printf("The value of numberu16 is: %d.\n", numberu16)
// output: The value of numberu16 is: 5.
var numberu32 uint32 = 6
fmt.Printf("The value of numberu32 is: %d.\n", numberu32)
// output: The value of numberu32 is: 6.
var numberu64 uint64 = 7
fmt.Printf("The value of numberu64 is: %d.\n", numberu64)
// output: The value of numberu64 is: 7.
type MyInt int
var myNumber MyInt = MyInt(number)
fmt.Printf("The value of myNumber is: %d.\n", myNumber)
// output: The value of myNumber is: 0.
}
%b,%#b
格式化整型
%b输出整数类型对应的二进制表示形式。%b可以将int, int32, int64, uint, uint8, uint16, uint32, uint64和与它们底层类型相同的类型输出。
package main
import "fmt"
func main() {
var number int = -1
fmt.Printf("The value of number is: %b.\n", number)
// output: The value of number is: -1.
var number32 int32 = 1
fmt.Printf("The value of number32 is: %b.\n", number32)
// output: The value of number32 is: 1.
var number64 int64 = 2
fmt.Printf("The value of number64 is: %b.\n", number64)
// output: The value of number64 is: 10.
var numberu uint = 3
fmt.Printf("The value of numberu is: %b.\n", numberu)
// output: The value of numberu is: 11.
var numberu8 uint8 = 4
fmt.Printf("The value of numberu8 is: %b.\n", numberu8)
// output: The value of numberu8 is: 100.
var numberu16 uint16 = 5
fmt.Printf("The value of numberu16 is: %b.\n", numberu16)
// output: The value of numberu16 is: 101.
var numberu32 uint32 = 6
fmt.Printf("The value of numberu32 is: %b.\n", numberu32)
// output: The value of numberu32 is: 110.
var numberu64 uint64 = 7
fmt.Printf("The value of numberu64 is: %b.\n", numberu64)
// output: The value of numberu64 is: 111.
type MyInt int
var myNumber MyInt = MyInt(number)
fmt.Printf("The value of myNumber is: %b.\n", myNumber)
// output: The value of myNumber is: -1.
}
与%b类似,%#b会在输出的结果添加0b前缀:
package main
import "fmt"
func main() {
fmt.Printf("%#b\n", 23)
// output: 0b10111
}
%o,%#o
%o输出整数类型对应的八进制表示形式。%o可以将int, int32, int64, uint, uint8, uint16, uint32, uint64和与它们底层类型相同的类型输出。
package main
import "fmt"
func main() {
var number int = -10
fmt.Printf("The value of number is: %o.\n", number)
// output: The value of number is: -12.
var number32 int32 = 23
fmt.Printf("The value of number32 is: %o.\n", number32)
// output: The value of number32 is: 27.
var number64 int64 = 32
fmt.Printf("The value of number64 is: %o.\n", number64)
// output: The value of number64 is: 40.
var numberu uint = 64
fmt.Printf("The value of numberu is: %o.\n", numberu)
// output: The value of numberu is: 100.
var numberu8 uint8 = 128
fmt.Printf("The value of numberu8 is: %o.\n", numberu8)
// output: The value of numberu8 is: 200.
var numberu16 uint16 = 216
fmt.Printf("The value of numberu16 is: %o.\n", numberu16)
// output: The value of numberu16 is: 330.
var numberu32 uint32 = 512
fmt.Printf("The value of numberu32 is: %o.\n", numberu32)
// output: The value of numberu32 is: 1000.
var numberu64 uint64 = 1024
fmt.Printf("The value of numberu64 is: %o.\n", numberu64)
// output: The value of numberu64 is: 2000.
type MyInt int
var myNumber MyInt = MyInt(number)
myNumber = 2048
fmt.Printf("The value of myNumber is: %o.\n", myNumber)
// output: The value of myNumber is: 4000.
}
与%o类似,%#o会在输出的结果添加前缀0:
package main
import "fmt"
func main() {
fmt.Printf("%#o\n", 23)
// output: 027
}
%x, %#x
格式化整型
%x输出整型的十六进制形式,%x可以将int, int32, int64, uint, uint8, uint16, uint32, uint64和与它们底层类型相同的类型输出。
package main
import "fmt"
func main() {
var number int = -10
fmt.Printf("The value of number is: %x.\n", number)
// output: The value of number is: -a.
var number32 int32 = 23
fmt.Printf("The value of number32 is: %x.\n", number32)
// output: The value of number32 is: 17.
var number64 int64 = 32
fmt.Printf("The value of number64 is: %x.\n", number64)
// output: The value of number64 is: 20.
var numberu uint = 64
fmt.Printf("The value of numberu is: %x.\n", numberu)
// output: The value of numberu is: 40.
var numberu8 uint8 = 128
fmt.Printf("The value of numberu8 is: %x.\n", numberu8)
// output: The value of numberu8 is: 80.
var numberu16 uint16 = 216
fmt.Printf("The value of numberu16 is: %x.\n", numberu16)
// output: The value of numberu16 is: d8.
var numberu32 uint32 = 512
fmt.Printf("The value of numberu32 is: %x.\n", numberu32)
// output: The value of numberu32 is: 200.
var numberu64 uint64 = 1024
fmt.Printf("The value of numberu64 is: %x.\n", numberu64)
// output: The value of numberu64 is: 400.
type MyInt int
var myNumber MyInt = MyInt(number)
myNumber = 2048
fmt.Printf("The value of myNumber is: %x.\n", myNumber)
// output: The value of myNumber is: 800.
}
格式化字符
如果对应的参数是字符,那么%x会将对应的Unicode码值转换为16进制:
package main
import "fmt"
func main() {
c := 'a'
fmt.Printf("%x, %x\n", c, 97)
// output: 61, 61
}
格式化字符串
如果为空字符串,则%x不进行任何转换:
package main
import "fmt"
func main() {
s := ""
fmt.Printf("--%x--%x--", s, 97)
// output: ----61--
}
对于长度大于1的字符串,%x将每个字符对应的Unicode码值转换成十六进制,然后将这些十六进制字符串拼接起来,而不是累加:
package main
import "fmt"
func main() {
s := "aa"
fmt.Printf("--%x--%x--", s, 97)
// output: --6161--61--
}
最后与%x类似,%#x会在输出的结果添加前缀0x:
package main
import "fmt"
func main() {
fmt.Printf("%#x\n", 23)
// output: 0x17
}
%X, %#X
%X与%x功能类似,只是将%x输出后的小写字母转换为大写。
package main
import "fmt"
func main() {
number := 30
fmt.Printf("%X\n", number)
// output: 1E
character := 'b'
fmt.Printf("%X\n", character)
// output: 62
position := "zoo"
fmt.Printf("%X\n", position)
// output: 7A6F6F
}
%v
%v输出值的本来格式,即值的相应默认格式。
package main
import "fmt"
func main() {
number := 20
fmt.Printf("%v~%v\n", number, &number)
// output: 20~0xc00000a0b0
floatNumber := 23.12
fmt.Printf("%v\n", floatNumber)
// output: 23.12
slice := []int{3, 4, 5}
fmt.Printf("%v\n", slice)
// output: [3 4 5]
array := [2]bool{true, false}
fmt.Printf("%v\n", array)
// output: [true false]
m := make(map[string]interface{})
m["date"] = "2021-05-08T14:38:00"
m["type"] = "Golang"
fmt.Printf("%v\n", m)
// output: map[date:2021-05-08T14:38:00 type:Golang]
channel := make(chan int, 2)
channel <- 2
fmt.Printf("%v\n", channel)
// output: 0xc00003e070
}
%+v
%v输出值的默认格式,当遇到结构体类型时,%v仅将结构体的成员变量输出,但不会输出成员变量和成员类型的对应关系,而%+v则会做到这一点。除此之外,%+v与%v没有什么不同。
package main
import "fmt"
type Demo struct {
a int
s string
}
func main() {
d := Demo{1, "q"}
fmt.Printf("v: %v, %v\n", d, &d)
// output: v: {1 q}, &{1 q}
fmt.Printf("v+: %+v, %+v\n", d, &d)
// output: +v: {a:1 s:q}, &{a:1 s:q}
}
%#v
%#v输出Go语法格式的值,通常在debug的过程中,%#v要远比%v好用的多,因为它能显示更多有用的信息。看看它与%+v有什么不同之处:
package main
import "fmt"
type Demo struct {
a int
s string
}
func main() {
d := Demo{1, "q"}
fmt.Printf("v: %v, %v\n", d, &d)
// output: v: {1 q}, &{1 q}
fmt.Printf("v+: %+v, %+v\n", d, &d)
// output: +v: {a:1 s:q}, &{a:1 s:q}
fmt.Printf("#v: %#v, %#v\n", d, &d)
// output: #v: main.Demo{a:1, s:"q"}, &main.Demo{a:1, s:"q"}
}
自定义样式
通过实现fmt.GoStringer接口来定制类型的%#v输出。下面的代码定义了一个自定义类型MyInteger,并实现了fmt.GoStringer接口,返回一个字符串"funny"。因此无论该类型变量中实际存储的值是什么,%#v得到的格式化字符串永远都为"funny",即fmt.GoStringer接口的返回值。
package main
import "fmt"
type MyInteger int
func (i MyInteger) GoString() string {
return "funny"
}
func main() {
var demo1 MyInteger = 23
fmt.Printf("%#v\n", demo1)
// output: funny
var demo2 MyInteger = 298
fmt.Printf("%#v\n", demo2)
// output: funny
}
%t
%t用于格式化布尔值或结果为布尔值的表达式的输出。
package main
import "fmt"
func main() {
fmt.Printf("%t\n", true)
// output: true
fmt.Printf("%t\n", false)
// output: false
a := true
b := false
fmt.Printf("%t, %t\n", a, b)
// output: true, false
fmt.Printf("%t\n", 1 == 2)
// output: false
}
%T
%T输出参数对应的Go语言类型。
package main
import (
"fmt"
"os"
)
func main() {
fmt.Printf("%T\n", true)
// output: bool
file, err := os.Open("student.json")
fmt.Printf("%T, %T\n", file, err)
// output: *os.File, <nil>
}
%%
两个百分号(%%)用来表示一个%输出。
package main
import "fmt"
func main() {
fmt.Printf("%%")
// output: %
}
%c
输出Unicode码对应的字符。
package main
import "fmt"
func main() {
fmt.Printf("%c", 971)
// output: ϋ
}
%U
将一个Unicode值转换为对应的Unicode码点。
package main
import (
"fmt"
)
func main() {
fmt.Printf("%U\n", 1)
// output: U+0001
}
%f
格式化浮点数,默认情况下,最多只能格式化小数点后六位。如果小数部分大于6位,则进行四舍五入保留前六位格式化。
package main
import (
"fmt"
)
func main() {
fmt.Printf("%f\n", 3.1415926)
// output: 3.141593
}
%e
%e与%f一样,都会对浮点数进行格式化。与%f不同的是,%e将浮点数格式化为科学计数法显示。
package main
import "fmt"
func main() {
fmt.Printf("%e", 1234123.312)
// output: 1.234123e+06
}
格式化宽度
在%和格式化类型之间添加一个自然数,用来表示显示宽度。例如%20s表示将被格式化的字符串显示20个宽度。
package main
import "fmt"
func main() {
fmt.Printf("---%20s---\n", "TTimeCat")
fmt.Printf("%3d\n", 23)
// outputs:
// --- TTimeCat---
// 23
}
如果格式化后的字符串长度超过了规定的字符显示宽度,那么规定的显示宽度失效:
package main
import "fmt"
func main() {
fmt.Printf("---%1s---\n", "TTimeCat")
// output: ---TTimeCat---
}
指定精度
对于浮点数,我们可以指定精度显示:
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%3.9f\n", math.Pi)
fmt.Printf("%3.10f\n", math.Pi)
// outputs:
// 3.141592654
// 3.1415926536
}
通过这个例子可以看出,指定精度的格式化会自动的四舍五入。对于小数部分长度没超过精度要求的浮点数,会在末尾补零:
package main
import "fmt"
func main() {
pi := 3.14
fmt.Printf("%.5f\n", pi)
// output: 3.14000
}
左对齐
默认情况下格式化字符串的对齐方式是右对齐。添加“-”可以设置为左对齐。
package main
import "fmt"
func main() {
name := "TTimeCat"
fmt.Printf("My name is %10s.\n", name)
fmt.Printf("My name is %-10s.\n", name)
// outputs:
// My name is TTimeCat.
// My name is TTimeCat .
}
参数和参数列表
每一个格式化样式字符一个参数。多个参数以逗号分隔,组成参数列表。参数的个数、顺序和类型必须与格式化字符一一对应,否则回出现格式化字符串没有达到预期的效果。
格式化字符与参数数量不匹配
下面的例子是一个参数与格式化字符数量不匹配的问题:
package main
import "fmt"
func main() {
fmt.Printf("Hi, My name is: %s. I am %d years old.", "TCatTime")
// output: Hi, My name is: TCatTime. I am %!d(MISSING) years old.
}
从上面的例子可以看出,参数数量小于格式化字符的数量时,得到的格式化字符串会显示没有匹配到参数的格式化字符串。如果参数数量大于格式化字符的数量,得到的格式化字符串会在末尾加上相关的提示:
package main
import "fmt"
func main() {
fmt.Printf("Hi, My name is: %s. I am %d years old.", "TCatTime", 15, 23)
// output: Hi, My name is: TCatTime. I am 15 years old.%!(EXTRA int=23)
}
格式化字符与参数类型不匹配
当格式化字符与参数类型不匹配时,也会影响格式化字符串的结果。例如把一个整型数字和%s匹配:
package main
import "fmt"
func main() {
fmt.Printf("I am %s years old.", 15)
// output: I am %!s(int=15) years old
}