Go语言中的格式化输出

404 阅读4分钟

格式化

在大多数情况下,我们会希望字符串具有更好的可读性,这不可避免的会遇到格式化问题,Go语言为我们提供了许多的格式化样式。

通常格式化样式都是%开头,并且搭配printf函数使用,在Go语言中,最常用的有fmt.Sprinf,fmt.Fprintf,fmt.printf

一些需要知道的概念:

  • 0b - 二进制
  • 0o - 八进制
  • 0d - 十进制
  • 0x - 十六进制

通常一个二进制数2被表示为10,它的完整表示形式为0b10,其他进制数以此类推

格式字符

0格式化描述接收类型
1%%输出百分号%任意类型
2%s输出string/[] bytestring,[] 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语言中并没有提供八进制表示规格化浮点数的实现。

\