13.1 打印文本
在 fmt
包中,Print
函数用于打印(输出)文本信息。依据输出目标的不同,Print
函数可以划分为三组,详见下表。
按应用目标分组 | 函数 | 说明 |
---|---|---|
将文本信息输出到标准输出流,一般是输出到屏幕上 | Print |
将传入函数的对象转化为字符串,以默认格式直接输出到屏幕上 |
同上 | Println |
直接输出对象,在末尾自动加上换行符 |
同上 | Printf |
可以通过格式控制符定制对象的输出形式 |
将文本输出到文件,或者实现了 io.Writer 接口的对象 |
Fprint |
以默认格式直接输出对象 |
同上 | Fprintln |
以默认格式直接输出对象,末尾自动加上换行符 |
同上 | Fprintf |
使用格式控制符定制对象的输出格式 |
生成文本信息,仅以字符串形式返回给调用者,无输出 | Sprint |
用默认格式将对象转化为字符串,并返回给调用者 |
同上 | Sprintln |
用默认格式将对象转化为字符串,并在末尾加上换行符,然后将该字符串返回 |
同上 | Sprintf |
用指定的格式控制符处理对象,并以字符串形式返回 |
13.2 格式化输出
Printf
、Fprintf
和 Sprintf
函数都可以使用格式控制符来格式化待输出的对象。Go 语言的格式控制符与 C 语言类似。格式化控制符以百分号(%
)开头,并用一个字母来表示数据类型,例如:
%s
:字符串类型%d
:十进制整数类型(int
、int8
、int64
等)%f
:浮点数类型(float32
、float64
)%c
:单个字符(rune
)%x
或%X
:十六进制数值
如果字符串中需要出现 “%
”,可以连用两个百分号(%%
),这样编译器不会将其作为格式控制符处理,而是直接输出百分号。例如:
fmt.Printf("上证指数上涨了%f%%", 0.52)
“%f
” 是格式控制符,格式化浮点数值 0.52
,“%%
” 不会进行任何格式化处理,只是输出字符 “%
”,所以最后输出的文本如下:
上证指数上涨了 0.520000%
13.2.1 格式化整数值
对于 int
、uint
、int32
、uint64
、int8
、uint16
等整数类型,可以使用特定于整数的格式化控制符来转化为文本。常用的整数值控制符有:
%b
:二进制数值%d
:十进制数值%o
:八进制数值,注意格式符号为小写字母o
%O
:八进制数值,带有"0o"
前缀,注意格式符号是大写字母O
%x
:十六进制数值,字母部分使用小写,例如6c5ea2
%X
:十六进制数值,字母部分使用大写,例如BC6E2F
请看下面示例:
var num int32 = 6037029
fmt.Println("int32 原数值:", num)
fmt.Printf("二进制:%b\n", num)
fmt.Printf("八进制:%o\n", num)
fmt.Printf("十进制:%d\n", num)
fmt.Printf("十六进制(小写):%x\n", num)
fmt.Printf("十六进制(大写):%X\n", num)
输出结果如下:
int32 原数值: 6037029
二进制:10111000001111000100101
八进制:27017045
十进制:6037029
十六进制(小写):5c1e25
十六进制(大写):5C1E25
格式控制符在字符串中可以看作占位符,输出的时候会用实际的数据内容替换控制符。例如:
fmt.Printf("我叫%s,今年%d岁\n", "小明", 21)
格式控制符 “%s
” 需要 string
类型的值,在生成输出文本时会被 “小明” 替换,“%d
” 表示需要整数类型的值(int
、int32
等),生成输出文本时被 21
替换,最后得到新的字符串 “我叫小明,今年 21 岁”。
13.2.2 格式化浮点数值
用于 float32
和 float64
类型的格式化控制符主要有以下几个:
%f
:标准浮点数,有小数点,但没有指数%e
或%E
:科学记数法,有指数部分。%e
表示指数部分使用小写字母(如1.123e+5
,+5
为指数),%E
表示指数部分用大写字母(如1.23E-7
,-7
为指数)%g
:用于格式化指数(科学记数法)较大的浮点数。指数小于6
时使用%f
格式,指数在6
以上(包含6
)时使用%e
格式
下面两行代码以科学记数法形式输出文本信息:
fmt.Printf("%e\n", 2357.123)
fmt.Printf("%E\n", 0.0000772)
结果如下:
2.357123e+03 // 2.357123×10³
7.720000E-05 // 7.72×10⁻⁵
下面的示例演示了 %g
控制符根据指数自动选择输出格式的情况:
var (
a float32 = 1.234567e-3
b float32 = 1.654321234e+7
)
fmt.Printf("%g\n%g\n", a, b)
结果如下:
0.001234567 // %f
1.6543212e+07 // %e
13.2.3 格式化字符串
对于 string
类型的值,格式化时应使用 %s
控制符,而 rune
类型(单个字符)的值则应使用 %c
控制符。下面的代码演示了 %s
控制符的用法:
fmt.Printf("今天的天气:%s\n", "中雨")
如果要格式化的对象是单个字符,可以用 %c
控制符。
var a, b, c, d, e = '春', '眠', '不', '觉', '晓'
fmt.Printf("%c %c %c %c %c\n", a, b, c, d, e)
如果被格式化的对象不是字符串类型却使用了 %s
控制符,程序在运行阶段不会报错,但会输出 “%!s
” 以提示被格式化的类型不是字符串。例如:
var h uint32 = 55660
fmt.Printf("幸运数字是: %s\n", h)
运行代码后输出的文本信息如下:
幸运数字是: %!s(uint32=55660)
若确实要以字符串形式输出 uint32
类型的值,可以将对象转换为字符串类型,以使类型匹配。
var h uint32 = 55660
// 将 32 位无符号整数值转换为字符串
hs := strconv.FormatUint(uint64(h), 10)
fmt.Printf("幸运数字是: %s\n", hs)
注意下面的转换方法会得到错误结果。
hs := string(h)
直接将整数值转换为字符串,得到的是编码与此整数值相等的 Unicode 字符,而不是与数值相同的字符串。
另外,使用 %q
控制符可以呈现带双引号(英文的双引号)的文本。例如:
var str = "Fade in"
fmt.Printf("Please check %q option", str)
输出文本如下:
Please check "Fade in" option
13.2.4 格式化布尔类型的值
%t
控制符支持将布尔类型的值转化为字符串 “true
” 或 “false
”。例如:
var r1 = 3 > 5
fmt.Printf("3 大于 5 吗? %t\n", r1)
var r2 = 100 % 13 != 0
fmt.Printf("100 ÷ 13 有余数吗? %t\n", r2)
输出结果如下:
3 大于 5 吗? false
100 ÷ 13 有余数吗? true
如果被格式化的对象不是字符串类型却使用了 %s
控制符,程序在运行阶段不会报错,但会输出 “%!s
” 以提示被格式化的类型不是字符串。例如:
var h uint32 = 55660
fmt.Printf("幸运数字是: %s\n", h)
运行代码后输出的文本信息如下:
幸运数字是: %!s(uint32=55660)
若确实要以字符串形式输出 uint32
类型的值,可以将对象转换为字符串类型,以使类型匹配。
var h uint32 = 55660
// 将 32 位无符号整数值转换为字符串
hs := strconv.FormatUint(uint64(h), 10)
fmt.Printf("幸运数字是: %s\n", hs)
注意下面的转换方法会得到错误结果。
hs := string(h)
直接将整数值转换为字符串,得到的是编码与此整数值相等的 Unicode 字符,而不是与数值相同的字符串。
另外,使用 %q
控制符可以呈现带双引号(英文的双引号)的文本。例如:
var str = "Fade in"
fmt.Printf("Please check %q option", str)
输出文本如下:
Please check "Fade in" option
13.2.5 %T 与 %v 格式控制符
要在输出的文本中呈现对象的类型,可以使用 %T
控制符。此处字母 T
为大写,注意与小写字母 t
的区别。%t
表示布尔值的字符串形式,%T
表示对象所属的类型名称。
下面的代码定义了 10 个变量并完成初始化,然后分别输出它们的类型。
var (
n1 = uint(15)
n2 = false
n3 = complex64(27 - 3i)
n4 = "hot"
n5 = int16(-7)
n6 = uint64(936600300)
n7 = float32(-0.93)
n8 = struct {
f1 uint
f2 byte
}{500, 20} // 匿名结构体
n9 = []byte("c4857da2")
n10 = int32(88)
)
// 输出变量值的类型
fmt.Printf("n1 的数据类型: %T\n", n1)
fmt.Printf("n2 的数据类型: %T\n", n2)
fmt.Printf("n3 的数据类型: %T\n", n3)
fmt.Printf("n4 的数据类型: %T\n", n4)
fmt.Printf("n5 的数据类型: %T\n", n5)
fmt.Printf("n6 的数据类型: %T\n", n6)
fmt.Printf("n7 的数据类型: %T\n", n7)
fmt.Printf("n8 的数据类型: %T\n", n8)
fmt.Printf("n9 的数据类型: %T\n", n9)
fmt.Printf("n10 的数据类型: %T\n", n10)
运行代码后,得到以下输出:
n1 的数据类型: uint
n2 的数据类型: bool
n3 的数据类型: complex64
n4 的数据类型: string
n5 的数据类型: int16
n6 的数据类型: uint64
n7 的数据类型: float32
n8 的数据类型: struct { f1 uint; f2 uint8 }
n9 的数据类型: []uint8
n10 的数据类型: int32
byte
类型实际上是 uint8
类型的别名,因此 %T
控制符输出的类型名称是 []uint8
,而不是 []byte
。
%v
控制符会使用默认的格式打印对象的值。此控制符有三种用法:
%v
:用默认的格式打印对象。%+v
:这主要是用于结构体对象。使用%v
只打印字段的值,若使用%+v
,则可以打印字段名称。%#v
:打印出来的对象值是一个有效的 Go 语言表达式。
下面的代码演示了 %v
、%+v
与 %#v
之间的区别。
type cat struct {
name string
age int
}
// 实例化 cat
var c = cat{"Jim", 3}
fmt.Printf("用 %v 控制符输出 cat 对象: %v\n", "%v", c)
fmt.Printf("用 %+v 控制符输出 cat 对象: %+v\n", "%+v", c)
fmt.Printf("用 %#v 控制符输出 cat 对象: %#v\n", "%#v", c)
运行结果如下:
用 %v 控制符输出 cat 对象: {Jim 3}
用 %+v 控制符输出 cat 对象: {name:Jim age:3}
用 %#v 控制符输出 cat 对象: main.cat{name:"Jim", age:3}
通过以上输出结果可以发现:当使用 %v
控制符时,只输出了两个字段的值——Jim 和 3;当使用 %+v
控制符时,输出了字段名 name
和 age
;当使用 %#v
控制符时,不仅输出了字段名和字段值,还输出了类型名称,而且 name
字段的值 Jim
也包含在一对双引号中。%#v
控制符输出的是一个 Go 语言表达式,若把此输出放到 Go 代码文档中,能够重新实例化 cat
对象。就像下面这样:
var x = main.cat{name:"Jim", age:3}
13.2.6 输出包含前缀的整数值
对二进制、八进制、十六进制的整数值,在格式化输出时,可以包含前缀。二进制数值以 “0b
” 开头;八进制数值以 “0
” 开头;十六进制数值以 “0x
” 或 “0X
” 开头。
在表示整数的格式控制符前加上 “#
”,表示输出为文本时加上前缀,例如 %#b
、%#x
等。十进制数值无前缀,因此不需要使用 %#d
。
下面的示例演示了 %#b
、%#o
、%#x
的用法。
var x = 35000
fmt.Printf("十进制: %d\n", x)
fmt.Printf("二进制: %#b\n", x)
fmt.Printf("八进制: %#o\n", x)
fmt.Printf("十六进制: %#x\n", x)
运行结果如下:
十进制: 35000
二进制: 0b1000100010111000
八进制: 0104270
十六进制: 0x88b8
对于十六进制,如果希望输出的文本中包含大写字母,可以使用 %#X
控制符。
13.2.7 设置输出内容的宽度
在格式控制符前加上一个整数值,用于设置对象被格式化后的文本长度(宽度)。例如 %15d
,表示将格式化十进制整数值,生成文本的长度为 15 个字符,如果实际使用的字符不够 15 个,其余空间将由空格填充。
下面的示例将演示设置输出内容宽度的方法。
步骤 1:定义 email
结构体。
type email struct {
from, to string
subject string
senddate string
}
步骤 2:创建 email
类型的切片实例,并初始化。
var msgs = []email{
email{
from: "jack@126.com",
to: "dg02@126.net",
subject: "进度表",
senddate: "2020-4-2",
},
email{
from: "abcd@test.org",
to: "let2915@21cn.com",
subject: "工序更改记录",
senddate: "2020-3-17",
},
email{
from: "ok365@opengnt.cn",
to: "37025562@tov.edu.cn",
subject: "课程安排",
senddate: "2019-12-24",
},
email{
from: "hack@123.net",
to: "6365056@qq.com",
subject: "最新地图",
senddate: "2020-3-6",
},
email{
from: "chenjin@163.com",
to: "doudou@1211.org",
subject: "送货清单",
senddate: "2019-7-12",
},
}
步骤 3:输出切片中所有 email
对象的字段值。
fmt.Printf("%12s %15s %18s %12s\n", "发件人", "收件人", "标题", "发送日期")
fmt.Println("----------------------------------------------------------------------")
for _, ml := range msgs {
fmt.Printf("%16s %24s %12s %15s\n", ml.from, ml.to, ml.subject, ml.senddate)
}
步骤 4:输出的结果如下:
发件人 收件人 标题 发送日期
-------------------------------------------------------------------------
jack@126.com dg02@126.net 进度表 2020-4-2
abcd@test.org let2915@21cn.com 工序更改记录 2020-3-17
ok365@opengnt.cn 37025562@tov.edu.cn 课程安排 2019-12-24
hack@123.net 6365056@qq.com 最新地图 2020-3-6
chenjin@163.com doudou@1211.org 送货清单 2019-7-12
步骤 5:如果宽度设置为负值,表示字符串左对齐(默认是右对齐)。
fmt.Printf("%-16s %-23s %-20s %-16s\n", "发件人", "收件人", "标题", "发送日期")
......
for _, ml := range msgs {
fmt.Printf("%-20s %-24s %-15s %-16s\n", ml.from, ml.to, ml.subject, ml.senddate)
}
步骤 6:再次运行示例,结果如下:
发件人 收件人 标题 发送日期
-------------------------------------------------------------------------
jack@126.com dg02@126.net 进度表 2020-4-2
abcd@test.org let2915@21cn.com 工序更改记录 2020-3-17
ok365@opengnt.cn 37025562@tov.edu.cn 课程安排 2019-12-24
hack@123.net 6365056@qq.com 最新地图 2020-3-6
chenjin@163.com doudou@1211.org 送货清单 2019-7-12
13.2.8 控制浮点数的精度
浮点数值的精度(保留的小数位)设置方法如下:
%.nf
n
表示要设置的精度,精度前面的小数点(.)不能省略。例如,%.3f
表示设置精度为 3(保留 3 位小数)。
下面的代码定义了一个浮点类型的变量并赋值,随后分别以 2、4、5 的精度将其输出。
var v = 13.50105485217
fmt.Printf("精度为 2:%.2f\n", v)
fmt.Printf("精度为 4:%.4f\n", v)
fmt.Printf("精度为 5:%.5f\n", v)
结果如下:
精度为 2:13.50
精度为 4:13.5011
精度为 5:13.50105
输出的字符宽度与浮点数精度可以同时使用,格式为:
%<宽度>.<精度>f
请看下面示例。
var v = 1234.8765432
fmt.Printf("宽度为 20,精度为 3:%20.3f\n", v)
fmt.Printf("宽度为 6,左对齐,精度为 2:%-6.2f\n", v)
输出内容如下:
宽度为 20,精度为 3: 1234.877
宽度为 6,左对齐,精度为 2:1234.88
13.2.9 参数索引
在调用类似 Printf
的函数时,用于格式化处理的对象将传递给函数的第二个参数。此参数个数可变,其索引从 1 开始计算(在 Printf
函数的参数列表中,从第二个参数开始传递对象)。
格式化控制符可以引用参数的索引,就像下面这样:
fmt.Printf("%[2]s: %[1]d", 120, "item")
%[2]s
会使用可变参数列表的第二个值,即字符串 “item”;同理,%[1]d
会使用整数值 120。最后格式化得到的文本信息为 “item:120”。
下面的代码将整数值 5000 依次以二进制、八进制、十六进制输出。
fmt.Printf("十进制:%d\n二进制:%#[1]b\n八进制:%#[1]o\n十六进制:%#[1]x\n", 5000)
第一个格式控制符 %d
默认会查找可变参数列表中的第一个参数,所以此控制符不需要添加索引。
得到的输出结果如下:
十进制:5000
二进制:0b1001110001000
八进制:011610
十六进制:0x1388
如果不使用索引,那么可变参数的个数和顺序必须跟字符串中格式化控制符出现的次数和顺序相同。上述例子中,如果不使用索引,那么整数值 5000 就要传递四次。
fmt.Printf("十进制:%d\n二进制:%#b\n八进制:%#o\n十六进制:%#x\n", 5000, 5000, 5000, 5000)
13.2.10 通过参数来控制文本的宽度和精度
对浮点数值进行格式化时,文本宽度值与精度值也可以通过索引从 Printf
函数的可变参数列表中获取相应的值。需要注意的是,如果 Printf
函数中的参数被用于设置文本宽度和精度,那么其类型必须是 int
。
用于设置文本宽度和精度的索引后面需要加上 “*
” 符号(星号),这是为了标识这些值不参与格式化处理。
下面的示例会通过变量 width
和 prec
来控制格式化后输出文本的宽度和精度,第一次调用 Printf
函数后,会修改变量的值,然后接着输出。
var (
width = 7 // 宽度
prec = 2 // 精度
)
var x float32 = -0.085216
// 第一次输出
fmt.Printf("%[1]*.[2]*f\n", width, prec, x)
// 修改宽度
width = 12
// 第二次输出
fmt.Printf("%[1]*.[2]*f\n", width, prec, x)
// 修改宽度和精度
width = 20
prec = 5
// 第三次输出
fmt.Printf("%[1]*.[2]*f\n", width, prec, x)
结果如下:
-0.09
-0.09
-0.08522
总结
输出
fmt
包中的 Print
函数可按输出目标分为三组:
- 输出到标准输出流(屏幕):
Print
(默认格式输出)、Println
(末尾加换行)、Printf
(格式控制输出)。 - 输出到文件或
io.Writer
接口对象:Fprint
、Fprintln
、Fprintf
。 - 生成字符串返回:
Sprint
、Sprintln
、Sprintf
。
格式控制符
Go 语言格式控制符以 %
开头,常见类型有:
- 字符串:
%s
- 整数:
%d
(十进制)、%b
(二进制)、%o
(八进制)、%x
/%X
(十六进制) - 浮点数:
%f
(标准)、%e
/%E
(科学记数法)、%g
(自动选择) - 字符:
%c
- 布尔:
%t
- 类型:
%T
- 默认值:
%v
、%+v
(结构体带字段名)、%#v
(Go 表达式)
特殊用法
- 输出前缀:二进制
%#b
(0b
)、八进制%#o
(0
)、十六进制%#x
/%#X
(0x
/0X
)。 - 设置宽度:
%15d
右对齐,%-15d
左对齐。 - 控制精度:
%.3f
保留 3 位小数,%20.3f
组合宽度和精度。 - 参数索引:
%[2]s: %[1]d
按索引引用参数。 - 动态宽度和精度:
%[1]*.[2]*f
从参数获取宽度和精度。