Name : Bob Address : New York Avenue Phone : 12345674567
这个问题听上去非常简单,简单到似乎不该成为一个问题,我们很容易地给出下面这段代码:
1 2 3 4 5 6 7 8
information := map[string]string{ "Name": "Bob", "Address": "New York Avenue", "Phone": "12345674567", } for k, v := range information { fmt.Printf("%-10s: %s\n", k, v) }
效果如下:
1 2 3
Name : Bob Address : New York Avenue Phone : 12345674567
好,下面我们希望打印的信息中可能参杂一些中文字符,使用相同的代码去打印:
1 2 3 4
Name : Bob Address : New York Avenue Phone : 12345674567 国籍 : 无国籍
可以看到不这么对齐了,这实在让人有点困惑。
而在我们继续往下之前,对 golang 比较熟悉的人应该都知道 character 和 byte 的区别,golang 之父 Rob Pike 也在这篇文章[1]中比较详尽地解释了差别。
对于一个中文字符,使用len()实际上计算的是其 byte 长度
1 2
fmt.Printf("the length of a is %d\n", len("a")) // the length of a is 1 fmt.Printf("the length of 我 is %d\n", len("我")) // the length of 我 is 3
而如果想要统计字符即 character 的数量,golang 提供了utf8.RuneCountInString
1 2 3
text := "你好,世界" fmt.Printf("the length of %s is %d\n", text, utf8.RuneCountInString(text)) // the length of 你好,世界 is 5
有没有可能fmt在计算中文这样的字符的长度时有问题呢?我们可以看一下fmt的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus). func(f *fmt) padString(s string) { if !f.widPresent || f.wid == 0 { f.buf.writeString(s) return } width := f.wid - utf8.RuneCountInString(s) if !f.minus { // left padding f.writePadding(width) f.buf.writeString(s) } else { // right padding f.buf.writeString(s) f.writePadding(width) } }
information := map[string]string{ "Name": "Bob", "Address": "New York Avenue", "Phone": "12345674567", "国籍": "无国籍", } for k, v := range information { kWid := runewidth.StringWidth(k) if kWid <= 10 { k += strings.Repeat(" ", 10-kWid) } fmt.Printf("%s: %s\n", k, v) }