格式化
在大多数情况下,我们会希望字符串具有更好的可读性,这不可避免的会遇到格式化问题,Go语言为我们提供了许多的格式化样式。
通常格式化样式都是%开头,并且搭配printf函数使用,在Go语言中,最常用的有fmt.Sprinf,fmt.Fprintf,fmt.printf
一些需要知道的概念:
- 0b - 二进制
- 0o - 八进制
- 0d - 十进制
- 0x - 十六进制
通常一个二进制数2被表示为10,它的完整表示形式为0b10,其他进制数以此类推
格式字符
| 0 | 格式化 | 描述 | 接收类型 |
|---|---|---|---|
| 1 | %% | 输出百分号% | 任意类型 |
| 2 | %s | 输出string/[] byte值 | string,[] byte |
| 3 | %q | 格式化字符串,输出的字符串两端有双引号"" | string,[] byte |
| 4 | %d | 输出十进制整型值 | 整型类型 |
| 5 | %f | 输出浮点数 | 浮点类型 |
| 6 | %e | 输出科学计数法形式 ,也可以用于复数 | 浮点类型 |
| 7 | %E | 与 **%e**相同 | 浮点类型 |
| 8 | %g | 根据实际情况判断输出%f或者%e,会去掉多余的0 | 浮点类型 |
| 9 | %b | 输出整型的二进制表现形式 | 数字类型 |
| 10 | %#b | 输出二进制完整的表现形式 | 数字类型 |
| 11 | %o | 输出整型的八进制表示 | 整型 |
| 12 | %#o | 输出整型的完整八进制表示 | 整型 |
| 13 | %x | 输出整型的小写十六进制表示 | 数字类型 |
| 14 | %#x | 输出整型的完整小写十六进制表示 | 数字类型 |
| 15 | %X | 输出整型的大写十六进制表示 | 数字类型 |
| 16 | %#X | 输出整型的完整大写十六进制表示 | 数字类型 |
| 17 | %v | 输出值原本的形式,多用于结构体输出 | 任意类型 |
| 18 | %+v | 输出结构体时将加上字段名 | 任意类型 |
| 19 | %#v | 输出完整Go语法格式的值 | 任意类型 |
| 20 | %t | 输出布尔值 | 布尔类型 |
| 21 | %T | 输出值对应的Go语言类型值 | 任意类型 |
| 22 | %c | 输出Unicode码对应的字符 | int32 |
| 23 | %U | 输出字符对应的Unicode码 | rune,byte |
| 24 | %p | 输出指针所指向的地址 | 指针类型 |
书写格式
一个标准的格式化字符由四个部分组成:
%: 百分号-/+: 对齐方式,+表示右对齐,-表示左对齐,默认前者对齐方式length: 格式长度保留长度: 仅仅对于浮点数与复数而言verb字符
fmt.Printf("%-10d%s\n", 255, "hello wrold")
结果
255 hello wrold
浮点数若设置精度大于类型表示的最大精度时会自动补零
fmt.Printf("%-5.100f\n", math.Pi)
结果
3.1415926535897931159979634685441851615905761718750000000000000000000000000000000000000000000000000000
示例
fmt.Printf("%%%s\n", "hello world") //1
fmt.Printf("%s\n", "hello world") //2
fmt.Printf("%q\n", "hello world") //3
fmt.Printf("%d\n", 2<<7-1) //4
fmt.Printf("%f\n", 1e2) //5
fmt.Printf("%e\n", 1e2) //6
fmt.Printf("%E\n", 1e2) //7
fmt.Printf("%g\n", 1e2) //8
fmt.Printf("%b\n", 2<<7-1) //9
fmt.Printf("%#b\n", 2<<7-1) //10
fmt.Printf("%o\n", 2<<7-1) //11
fmt.Printf("%#o\n", 2<<7-1) //12
fmt.Printf("%x\n", 2<<7-1) //13
fmt.Printf("%#x\n", 2<<7-1) //14
fmt.Printf("%X\n", 2<<7-1) //15
fmt.Printf("%#X\n", 2<<7-1) //16
type person struct {
name string
age int
address string
}
fmt.Printf("%v\n", person{"lihua", 22, "beijing"}) //17
fmt.Printf("%+v\n", person{"lihua", 22, "beijing"}) //18
fmt.Printf("%#v\n", person{"lihua", 22, "beijing"}) //19
fmt.Printf("%t\n", true) //20
fmt.Printf("%T\n", person{}) //21
fmt.Printf("%c%c\n", 20050, 20051) //22
fmt.Printf("%U\n", '码') //23
fmt.Printf("%p\n", &person{}) //24
错误情况
格式化字符数量 < 参数列表数量
fmt.Printf("", "") //%!(EXTRA string=)
格式化字符数量 > 参数列表数量
fmt.Printf("%s%s", "") //%!s(MISSING)
类型不匹配
fmt.Printf("%s", 1) //%!s(int=1)
缺少格式化动词
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)
特殊情况
当你使用%b来格式化一个浮点数时会得到一串十分令人匪夷所思的东西
fmt.Printf("%b", 1.0)
这行语句的运行结果是:4503599627370496p-52,是不是看起来这非常的离谱
其实不然,背后的原理 与IEEE 754 规定的浮点数二进制表示法有关
我们可以先看看用十六进制输出
fmt.Printf("%x\b", 1.0)
运行结果是0x1p+00,这下看起来是不是稍微有点规律了
十进制里浮点数有一个科学计数法,1.00 ~ 1.000000e0 +00
类似的,二进制里也有一个科学计数法,4503599627370496p-52这个数字看起来很吓人,其实
结果就是1.0 ~ 4503599627370496 * p^-52
这些十六进制中也说得通了,1.0 ~ 1 * 2^0
我们可以再来看一个例子:
fmt.Printf("%b\n", 3.7) // 8331659310635418p-51
fmt.Printf("%x\n", 3.7) // 0x1.d99999999999ap+01
这次的结果比第一次还要吓人一点,但是有了上次的经验,我们可以很快就解出来
我们可以先从16进制入手
0x1.d99999999999ap+01拆成3部分,0x1 .d99999999999a p+01
其中0x1 = 1 * 16^0 = 1
.d99999999999a = d * 16^-1 + 9 * 16^-2 +9 * 16^-2 + ...... + a * 16^-13 ~ 0.85
p+01 = 2^01 = 2
结果就是近似于3.7
规格化浮点数
IEE754 规格化浮点数即为: 符号位 * (阶码+尾数) * p^n
阶码为0或1
其中 1.000000 <= 尾数 <= 2.000000
p = 2 ,n为指数 且须是二进制形式
如此一来以上都说得通了
最后需要注意的是Go语言中并没有提供八进制表示规格化浮点数的实现。
\