String Formatting with the Fmt Package

Go标准库fmt package提供了多个打印函数,将数据以字符串的形式输出到终端,文件,或者其它的值(需要满足io.Writer interface), 以及其它的string. 这些函数如Table3.3所示。 有的print函数返回一个error. 通常我们会直接忽略掉打印到console的返回值, 但对于打印files, network连接则需要检查这个error.

fmt package也提供了各种输入函数(比如 fmt.Scan(), fmt.Scanf(), 和fmt.Scanln()),以读取来至console, files和 strings中的数据。这些函数将在第8章中讲到.

最简单的输出一个值是使用fmt.Print() 和 fmt.Println()函数(输出到os.Stdout, i.e., 到console), 或者 fmt.Fprint() 和 fmt.Fprintf() 输出到io.Writer(e.g., to a file), 或者 fmt.Sprint() 和 fmt.Sprintln()函数输出到一个string.

Table 3.3 The Fmt Package’s Print Functions

Syntax Description/result
fmt.Errorf(format, args...) 返加一个error值,它包含了由format和args组成的字符串
fmt.Fprint(writer,args...) 对于每一个args以%v的格式(如果arg为非字符串,则以空格分格)到 writer. 同时返回写入的字节,以及error或者nil,
fmt.Fprintf(writer,format, args...) 按照format的要求输出到writer, 并返回写入的字节数,和error 或者 nil
fmt.Fprintln(writer, args...) 以%v的格式输出每个args, 每个args以空格分隔,并在句后添加换行符, 同时返回字节数和error或nil
fmt.Print(args...) 以%v的格式输出到os.Stdout, 如果是非字符串,则以空格进行分隔。同时返回字节数, error 或 nil
fmt.Printf(format,args...) 以format格式输出args
fmt.Println(args...) 以%v输出,每个参数之间添加空格分格,在句尾添加换行符。 并返回字节数,error 或nil
fmt.Sprint(args...) 输出到string
fmt.Sprintf(format,args...) 输出到string
fmt.Sprintln(args...) 输出到string

Table3.4The Fmt Package’s Verbs

Verbs主要用于输出单个值. 如果要输出的值是一个slice, 则通常以方括号[]包围, 每个值以verb指定的格式输出,同时以空格分隔。如果要输出的值是一个map类型,则只可以使用 %v 或者 %#v, 除非key 和value是相同的类型, 在这种情况下,可以使用跟类型相兼容的verbs.

Verb Description/result
%% 输出%
%b 整型以2进制输出,或者更高级的, 对于浮点数,则以科学计数法输出power of 2 exponent
%c Unicode character
%d 10进制表示的整数
%e 浮点数或者实数,以e表示
%E 以E表示的浮点数或者实数
%f 以标准记数法表示多少位小数
%g 以%e或者%f表示,只为产生最紧凑的输出
%G 以%E或者%f表示
%o octal 八进制表示
%p 一个值的地址,以0x为前缀的16进制(a-f)
%q 字符串和[]byte,则加上"".
%s 输出字符串
%t true or false
%T value 类型
%U 使用的Unicode表式 U+00B6
%v 内置的默认格式,如果type's 存在String()方法,则调用String()
%x 整型的16进制表式。a-f
%X 16进制表式 A-F

Table 3.5 The Fmt Package’s Verb Modifiers

Modifier Description/result
space fmt.Printf("% X", "中") E4 B8 AD, 在每个字符之间添加一个空格, fmt.Printf("% d", n), 如果n为负数,则空格变为 '-', 则数则为空格
# 修改默认格式: 比如八进制(%#0) 添加一个前导 0, 十六进制(%#x)添加0x (%#X)添加0X
删除%p (%#p)中的0x
对于%q, 正常"\"hello\"" 而%#q为 `"hello"`输出原始字符串
对于"%u", 'x', 正常为 U+0078, %#U 则为 U+0078 'x'
%#v 如果要输出的类型为struct, 则会输出struct类型, 字段名, 可以通过Formatting for Debugging了解
+ 对于数字,强制输出符号(如果没有+号,正数不会输出 "+")
%q (%+q) 保证只输出ASCII(例子请查看Formatting Strings and Slices)
- 向左对齐, 默认为向右(空格添加在左边)
0 添加 0 作为前导,而不是默认的空格 fmt.Printf("%09d", 1) //00000001 如果为-1, -00000001
n.m 如果为数字,表示数值的输出时有多少个字符(如果为f loat,则包含了小数点及后面的数字)
如果为字符串 n 表示为最小的字符串宽度,如果字符串太短, 则添加空格. .m表示字符串最大的宽度, 如果字符串太长,将会被裁减n和n可以通过 * 来占位, 然后从arguments获得相应的值 fmt.Sprintf("%+.*f", x, 100.0) 如果传入参数x=2, 则为%+.2f // +100.00

格式化Booleans

fmt.Printf("%t %t\n", true, false)
//true false

如果要以1,0的形式输出true, false. 则必须使用自定义函数

func IntForBool(b bool) int {
  if b {
    return 1
  } else {
    return 0
  }
}
fmt.Printf("%d %d\n", IntForBool(true), IntForBool(false))
// 1·0

格式化Integers

二进制

fmt.Printf("|%b|%9b|%-9b|%09b|% 9b|\n", 37, 37, 37, 37, 37)
|100101|···100101|100101···|000100101|···100101|

八进制

fmt.Printf("|%o|%#o|%# 8o|%#+ 8o|%+08o|\n", 41, 41, 41, 41, -41)
|51|051|·····051|····+051|-0000051|

# 会在八进制数前面添加0, 在16进制前面添加0x (%#x) 或0X (%#X)
+ 用于强制输出符号,否则正数不会输出 +

十六进制

i := 3931
fmt.Printf("|%x|%X|%8x|%08x|%#04X|0x%04X|\n", i, i, i, i, i, i)
|f5b|F5B|·····f5b|00000f5b|0X0F5B|0x0F5B|

十进制

默认情况下,前导占位符只能是' ' 或者 0, 如果要输出 '*', 则需要自定义函数

func Pad(number, width int, pad rune) string {
s := fmt.Sprint(number)
gap := width - utf8.RuneCountInString(s)
if gap > 0 {
return strings.Repeat(string(pad), gap) + s
}
return s
}
i = 569
fmt.Printf("|$%d|$%06d|$%+06d|$%s|\n", i, i, i, Pad(i, 6, '*'))
|$569|$000569|$+00569|$***569|

格式化Characters

GO characters为 rune 或者 int32类型, 它可以以number输出,也可以Unicode 字符输出

fmt.Printf("%d %#04x %U '%c'\n", 0x3A6, 934, '\u03A6', '\U000003A6')
934·0x03a6·U+03A6·'Φ'

格式化浮点数

func Humanize(amount float64, width, decimals int,
pad, separator rune) string {
dollars, cents := math.Modf(amount)  //整数部分和小数部分,两者都为float64, 比如100.01 -> %f, 100.000000 0.010000 
whole := fmt.Sprintf("%+.0f", dollars)[1:] // Strip "±" 100
fraction := ""
if decimals > 0 {
fraction = fmt.Sprintf("%+.*f", decimals, cents)[2:] // Strip "±0"
}

sep := string(separator)
for i := len(whole) - 3; i > 0; i -= 3 {
whole = whole[:i] + sep + whole[i:]
}
if amount < 0.0 {
whole = "-" + whole
}
number := whole + fraction
gap := width - utf8.RuneCountInString(number)
if gap > 0 {
return strings.Repeat(string(pad), gap) + number
}
return number
}

for _, x := range []float64{-.258, 7194.84, -60897162.0218, 1.500089e-8} {
fmt.Printf("|%20.5e|%20.5f|%s|\n", x, x, Humanize(x, 20, 5, '*', ','))
}
|········-2.58000e-01|············-0.25800|************-0.25800|
|·········7.19484e+03|··········7194.84000|*********7,194.84000|
|········-6.08972e+07|·····-60897162.02180|***-60,897,162.02180|
|·········1.50009e-08|·············0.00000|*************0.00000|

Formating Strings and Slices

slogan := "End Óréttlæti♥"
fmt.Printf("%s\n%q\n%+q\n%#q\n", slogan, slogan, slogan, slogan)
End Óréttlæti♥
"End Óréttlæti♥"
"End \u00d3r\u00e9ttl\u00e6ti\u2665" //ASCII-only 
`End Óréttlæti♥`
//%+q 只会输出(U+0020, U+007E)之间的ASCII, 其它字符串都是输出转义字符
chars := []rune(slogan)
fmt.Printf("%x\n%#x\n%#X\n", chars, chars, chars)
[45·6e·64·20·d3·72·e9·74·74·6c·e6·74·69·2665]
[0x45·0x6e·0x64·0x20·0xd3·0x72·0xe9·0x74·0x74·0x6c·0xe6·0x74·0x69·0x2665]
[0X45·0X6E·0X64·0X20·0XD3·0X72·0XE9·0X74·0X74·0X6C·0XE6·0X74·0X69·0X2665]

对于大部分的数组来说, 输出的时候都是以[]包围,而[]byte 数组是个另外, 只在%v时,才会以[]包围

bytes := []byte(slogan)
fmt.Printf("%s\n%x\n%X\n% X\n%v\n", bytes, bytes, bytes, bytes, bytes)
End·Óréttlæti♥
456e6420c39372c3a974746cc3a67469e299a5
456E6420C39372C3A974746CC3A67469E299A5
45·6E·64·20·C3·93·72·C3·A9·74·74·6C·C3·A6·74·69·E2·99·A5
[69·110·100·32·195·147·114·195·169·116·116·108·195·166·116·105·226·153·165]

Formatting for Debugging

%T(type) 可以打印一个build-in或者自定义类型的值, %v 用于打印一个内制的值, 实际上 %v 也可以用于自定义类型的输出,如果自定义类型,没有String()方法,则输出默认值,如果有String方法,则调用String()方法

type polar struct{ radius, θ float64 }
p := polar{8.32, .49}
fmt.Print(-18.5, 17, "Elephant", -8+.7i, 0x3C7, '\u03C7', "a", "b", p)
fmt.Println()
fmt.Println(-18.5, 17, "Elephant", -8+.7i, 0x3C7, '\u03C7', "a", "b", p)

Output

-18.5·17Elephant(-8+0.7i)·967·967ab{8.32·0.49}
-18.5·17·Elephant·(-8+0.7i)·967·967·a·b·{8.32·0.49}
p := polar{-83.40, 71.60}
fmt.Printf("|%T|%v|%#v|\n", p, p, p)
fmt.Printf("|%T|%v|%t|\n", false, false, false)
fmt.Printf("|%T|%v|%d|\n", 7607, 7607, 7607)
fmt.Printf("|%T|%v|%f|\n", math.E, math.E, math.E)
fmt.Printf("|%T|%v|%f|\n", 5+7i, 5+7i, 5+7i)
s := "Relativity"
fmt.Printf("|%T|\"%v\"|\"%s\"|%q|\n", s, s, s, s)

Output

|main.polar|{-83.4·71.6}|main.polar{radius:-83.4,·θ:71.6}|
|bool|false|false|
|int|7607|7607|
|float64|2.718281828459045|2.718282|
|complex128|(5+7i)|(5.000000+7.000000i)|
|string|"Relativity"|"Relativity"|"Relativity"|

%v == fmt.Print(), 只是输出一个变量的默认格式,而%#v,则输出整个struct结构以及它的值。%T 主要用于调试自定义类型定义在哪个包中(在这里是main.polar)
byte 跟 unit8 是同义词,而rune 跟int32是同义词, 我们可以通过下面的Example来了解

s := "Alias↔Synonym"
chars := []rune(s)
bytes := []byte(s)
fmt.Printf("%T: %v\n%T: %v\n", chars, chars, bytes, bytes)

Output

[]int32: [65 108 105 97 115 8596 83 121 110 111 110 121 109]
[]uint8: [65 108 105 97 115 226 134 148 83 121 110 111 110 121 109]

Go 也可以输出任何值的内存地址,使用%p(pointer) verb

i := 5
f := -48.3124
s := "Tomás Bretón"
fmt.Printf("|%p → %d|%p → %f|%#p → %s|\n", &i, i, &f, f, &s, s)

Output

|0xf840000300·→·5|0xf840000308·→·-48.312400|f840001990·→·Tomás·Bretón|
//%#p 0x is dropped

输出slices 和 maps

fmt.Println([]float64{math.E, math.Pi, math.Phi})
fmt.Printf("%v\n", []float64{math.E, math.Pi, math.Phi})
fmt.Printf("%#v\n", []float64{math.E, math.Pi, math.Phi})
fmt.Printf("%.5f\n", []float64{math.E, math.Pi, math.Phi})

Output

[2.718281828459045·3.141592653589793·1.618033988749895]
[2.718281828459045·3.141592653589793·1.618033988749895]
[]float64{2.718281828459045,·3.141592653589793,·1.618033988749895}
[2.71828·3.14159·1.61803]
fmt.Printf("%q\n", []string{"Software patents", "kill", "innovation"})
fmt.Printf("%v\n", []string{"Software patents", "kill", "innovation"})
fmt.Printf("%#v\n", []string{"Software patents", "kill", "innovation"})
fmt.Printf("%17s\n", []string{"Software patents", "kill", "innovation"})

Output

["Software·patents"·"kill"·"innovation"]
[Software·patents·kill·innovation]
[]string{"Software·patents",·"kill",·"innovation"}
[·Software·patents··············kill········innovation]
fmt.Printf("%v\n", map[int]string{1: "A", 2: "B", 3: "C", 4: "D"})
fmt.Printf("%#v\n", map[int]string{1: "A", 2: "B", 3: "C", 4: "D"})
fmt.Printf("%v\n", map[int]int{1: 1, 2: 2, 3: 4, 4: 8})
fmt.Printf("%#v\n", map[int]int{1: 1, 2: 2, 3: 4, 4: 8})
fmt.Printf("%04b\n", map[int]int{1: 1, 2: 2, 3: 4, 4: 8})

Output

map[4:D·1:A·2:B·3:C]
map[int]·string{4:"D",·1:"A",·2:"B",·3:"C"}
map[4:8·1:1·2:2·3:4]
map[int]·int{4:8,·1:1,·2:2,·3:4}
map[0100:1000·0001:0001·0010:0010·0011:0100]