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]