Go Printf 如何进行格式化输出,结构体格式化输出,一站式解决所有烦恼

Go fmt包

fmt包实现了格式化I/O函数,类似于C的 printf 和 scanf 。 格式”占位符“衍生自C,但是比C更简单。

1. Printf 和 Println 的区别与用法

Println 与 Printf 都是fmt包中的方法,在需要打印信息时需要用到这两个函数,有什么区别呢?

  • Println : 可以打印出字符串和变量
  • Printf :只可以打印出格式化的字符串,可以输出字符串类型的变量,不可以输出整形变量和整形。

2. Printf 详细用法

常用格式化输出示例:

fmt.Printf("start at number %v, end count %v \n", start, end)

2.1 占位符

一般占位符

符号说明
%v相应值的默认格式
%+v在打印结构体时,默认格式,会添加字段名
%#v相应值的Go语法表示
%T相应值的类型的Go语法表示
%%字面上的百分号,并非值的占位符

布尔占位符

符号说明
%ttrue 或 false

整数占位符

符号说明
%b二进制表示
%c相应Unicode码所表示的字符
%d十进制表示
%o八进制表示
%q单引号围绕的字符字面值,由Go语言安全的转义
%x十六进制表示,字母形式未小写a-f
%X十六进制表示,字母形式未大写A-F
%UUnicode格式:U+1234,等同于 ”U+%04X“

浮点数及其复合构成占位符

符号说明
%b无小数部分,指数为二的幂的科学计数法
%e科学计数法
%E科学计数法
%f有小数点而无指数
%g根据情况选择%e或%f产生更紧凑的 无末尾0的输出
%G根据情况选择%E或%f产生更紧凑的 无末尾0的输出

字符串和字节切片占位符

符号说明
%s字符串或切片的
%q双引号围绕的字符串,由Go语法安全转义
%x十六进制,小写字母,每字节两个字符
%X十六进制,大写字母,每字节两个字符

指针

符号说明
%p十六进制表示,前缀0x

其他标记

符号说明
+总打印数值的正负号;对于 %q(%+q)保证只输出 ASCII 编码的字符
-在右侧而非左侧填充空格(左对齐该区域)
#备用格式:对八进制添加前导 0(%#o),对十六进制添加前导 0x(%#x)或 0X(%#X)对 %p(%#p)去掉前导 0x

宽度和精度

宽度是%之后的值,如果没有指定,则使用该值的默认值,精度是跟在宽度之后的值,如果没有指定,也使用要打印的值的默认精度。%9.2f, 宽度9,精度2.

%fdefault width, default precision
%9fwidth 9, default precision
%.2fdefault witdh, precision 2
%9.2fwidth 9, precision 2
%9.fWidth 9, precision 0

对数值而言,宽度为该数值占用区域的最小宽度;精度为小数点之后的位数。但对于 %g/%G 而言,精度为所有数字的总数。例如,对于123.45,格式 %6.2f会打印123.45,而 %.4g 会打印123.5。%e 和 %f 的默认精度为6;但对于 %g 而言,它的默认精度为确定该值所必须的最小位数。

对大多数值而言,宽度为输出的最小字符数,如果必要的话会为已格式化的形式填充空格。对字符串而言,精度为输出的最大字符数,如果必要的话会直接截断。

宽度是指"必要的最小宽度". 若结果字符串的宽度超过指定宽度时, 指定宽度就会失效。

2.2 扫描

一组类似的函数通过扫描已格式化的文本来产生值。Scan、Scanf 和 Scanln 从os.Stdin 中读取;Fscan、Fscanf 和 Fscanln 从指定的 io.Reader 中读取;Sscan、Sscanf 和 Sscanln 从实参字符串中读取。Scanln、Fscanln 和 Sscanln在换行符处停止扫描,且需要条目紧随换行符之后;Scanf、Fscanf 和 Sscanf需要输入换行符来匹配格式中的换行符;其它函数则将换行符视为空格。

Scanf、Fscanf 和 Sscanf 根据格式字符串解析实参,类似于 Printf。例如,%x会将一个整数扫描为十六进制数,而 %v 则会扫描该值的默认表现格式。

格式化类似于 Printf,但也有例外,如下所示:

%p 没有实现
%T 没有实现
%e %E %f %F %g %G 都完全等价,且可扫描任何浮点数或复合数值
%s 和 %v 在扫描字符串时会将其中的空格作为分隔符
标记 # 和 + 没有实现
在输入Scanf中,宽度可以理解成输入的文本(%5s表示输入5个字符),而Scanf没有精度这种说法(没有%5.2f,只有 %5f)

2.3. Printf 使用示例

package main
import "fmt"
import "os"
type point struct {
    x, y int
}
func main() {
    //Go 为常规 Go 值的格式化设计提供了多种打印方式。例如,这里打印了 point 结构体的一个实例。
    p := point{1, 2}
    fmt.Printf("%v\n", p) // {1 2}
    //如果值是一个结构体,%+v 的格式化输出内容将包括结构体的字段名。
    fmt.Printf("%+v\n", p) // {x:1 y:2}
    //%#v 形式则输出这个值的 Go 语法表示。例如,值的运行源代码片段。
    fmt.Printf("%#v\n", p) // main.point{x:1, y:2}
    //需要打印值的类型,使用 %T。
    fmt.Printf("%T\n", p) // main.point
    //格式化布尔值是简单的。
    fmt.Printf("%t\n", true)
    //格式化整形数有多种方式,使用 %d进行标准的十进制格式化。
    fmt.Printf("%d\n", 123)
    //这个输出二进制表示形式。
    fmt.Printf("%b\n", 14)
    //这个输出给定整数的对应字符。
    fmt.Printf("%c\n", 33)
    //%x 提供十六进制编码。
    fmt.Printf("%x\n", 456)
    //对于浮点型同样有很多的格式化选项。使用 %f 进行最基本的十进制格式化。
    fmt.Printf("%f\n", 78.9)
    //%e 和 %E 将浮点型格式化为(稍微有一点不同的)科学技科学记数法表示形式。
    fmt.Printf("%e\n", 123400000.0)
    fmt.Printf("%E\n", 123400000.0)
    //使用 %s 进行基本的字符串输出。
    fmt.Printf("%s\n", "\"string\"")
    //像 Go 源代码中那样带有双引号的输出,使用 %q。
    fmt.Printf("%q\n", "\"string\"")
    //和上面的整形数一样,%x 输出使用 base-16 编码的字符串,每个字节使用 2 个字符表示。
    fmt.Printf("%x\n", "hex this")
    //要输出一个指针的值,使用 %p。
    fmt.Printf("%p\n", &p)
    //当输出数字的时候,你将经常想要控制输出结果的宽度和精度,可以使用在 % 后面使用数字来控制输出宽度。默认结果使用右对齐并且通过空格来填充空白部分。
    fmt.Printf("|%6d|%6d|\n", 12, 345)
    //你也可以指定浮点型的输出宽度,同时也可以通过 宽度.精度 的语法来指定输出的精度。
    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
    //要最对齐,使用 - 标志。
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
    //你也许也想控制字符串输出时的宽度,特别是要确保他们在类表格输出时的对齐。这是基本的右对齐宽度表示。
    fmt.Printf("|%6s|%6s|\n", "foo", "b")
    //要左对齐,和数字一样,使用 - 标志。
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
    //到目前为止,我们已经看过 Printf了,它通过 os.Stdout输出格式化的字符串。Sprintf 则格式化并返回一个字符串而不带任何输出。
    s := fmt.Sprintf("a %s", "string")
    fmt.Println(s)
    //你可以使用 Fprintf 来格式化并输出到 io.Writers而不是 os.Stdout。
    fmt.Fprintf(os.Stderr, "an %s\n", "error")
}

3. 结构体格式化输出

在软件系统中定位问题时日志不可或缺,但是当一个系统功能繁多,需要打印的日志也多如牛毛,此时为了提高我们浏览日志的效率,便于阅读的输出格式必不可少。

打印结构体是打印日志时最长见的操作,但是当结构体内容较多都在一行时,不易于阅读。在 Go 中结构体可以方便的转为 JSON,因此我们可以借助 JSON 完成对 struct 的格式化输出。

3.1 输出结构体字段(%+v)

package main

import (
	"fmt"
)

// Student 学生信息
type Student struct {
	Name   string
	Addr   HomeInfo
	M      map[string]string
}

// HomeInfo 家庭住址
type HomeInfo struct {
	Province     string
	City         string
	County       string
	Street       string
	DetailedAddr string
}

var student = Student{
	Name: "dablelv",
	Addr: HomeInfo{
		Province:     "Guangdong",
		City:         "Shenzhen",
		County:       "Baoan",
		Street:       "Xixiang",
		DetailedAddr: "Shengtianqi",
	},
	M: map[string]string{
		"hobby": "pingpopng",
	},
}

func main() {
	fmt.Printf("student=%+v\n", student)
}

输出结果:

student={Name:cat Addr:{Province:Guangdong City:Shenzhen County:Baoan Street:Xixiang DetailedAddr:Shengtianqi} M:map[hobby:pingpopng]}

这种输出方式虽然可以将结构体的字段名称打印出来,但是当结构体字段多,嵌套层次深时,扎堆输出在一行时不便于阅读。

3.2 输出格式化 JSON 串

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

func main() {
	bs, _ := json.Marshal(student)
	var out bytes.Buffer
	json.Indent(&out, bs, "", "\t")
	fmt.Printf("student=%v\n", out.String())
}

输出结果:

student={
        "Name": "cat",
        "Addr": {
                "Province": "Guangdong",
                "City": "Shenzhen",
                "County": "Baoan",
                "Street": "Xixiang",
                "DetailedAddr": "Shengtianqi"
        },
        "M": {
                "hobby": "pingpopng"
        }
}

将struct 转化为 json串后再格式化输出,大大增加了可读性。

3.3 使用 go-huge-util

package main

import (
	"fmt"
	
    huge "github.com/dablelv/go-huge-util"
)

func main() {
	s, _ := huge.ToIndentJSON(&student)
	fmt.Printf("student=%v\n", s)
}

输出结果:

student={
        "Name": "cat",
        "Addr": {
                "Province": "Guangdong",
                "City": "Shenzhen",
                "County": "Baoan",
                "Street": "Xixiang",
                "DetailedAddr": "Shengtianqi"
        },
        "M": {
                "hobby": "pingpopng"
        }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值