Go中字符串填充方法

2,699 阅读4分钟

如果你希望你的Go中的字符串被打印成对齐的列,你需要添加填充,即在字符串的开头或结尾处填充空白字符,以便输出有固定数量的字符。要做到这一点,你可以使用特殊标志,如"%-10s" ,在格式化文本时添加到格式化"动词 "中。你也可以使用一个外部包或内置的 text/tabwriter来打印对齐的列中的数据。

打印一个带有填充物的字符串,使用 fmt

使用内置的 fmt.Printf()函数,你可以使用所谓的动词来设置打印数据的格式,如"%s" 。此外,还有几个标志可以改善字符串的显示效果。其中一个是添加填充的标志,即在动词"%s" 中指定其大小的数字,在"%""s" 之间。默认情况下,字符串在左边填充了空格(文本是右对齐的)。如果你想要右填充和左对齐,请在填充大小之前添加- 标志。

例子:

格式描述
"%s"标准的字符串格式
"%20s"用左边的空格填充字符串(右对齐的字符串)
"%-20s"用右边的空格来填充字符串(左对齐字符串)

左侧填充的例子

// left padding
fmt.Printf("+%s+\n", strings.Repeat("-", 45))
fmt.Printf("| %20s | %20s |\n", "vegetables", "fruits")
fmt.Printf("|%s|\n", strings.Repeat("-", 45))
fmt.Printf("| %20s | %20s |\n", "potato", "strawberry")
fmt.Printf("+%s+\n", strings.Repeat("-", 45))

输出

+---------------------------------------------+
| vegetables | fruits |
|---------------------------------------------|
| potato | strawberry |
+---------------------------------------------+

右边填充的例子

// right padding
fmt.Printf("+%s+\n", strings.Repeat("-", 45))
fmt.Printf("| %-20s | %-20s |\n", "vegetables", "fruits")
fmt.Printf("|%s|\n", strings.Repeat("-", 45))
fmt.Printf("| %-20s | %-20s |\n", "potato", "strawberry")
fmt.Printf("+%s+\n", strings.Repeat("-", 45))

输出

+---------------------------------------------+
| vegetables | fruits |
|---------------------------------------------|
| potato | strawberry |
+---------------------------------------------+

可变的padding大小

但是,如果我们想打印一个有不同宽度的列的大表,其大小将适应字符串的长度,该怎么办?在这种情况下,填充的大小将是可变的,但这对该 fmt.Printf()函数来说不是问题。下面的例子显示了如何做到这一点。

package main
import (
"fmt"
"strings"
)
func printTable(table [][]string) {
// get number of columns from the first table row
columnLengths := make([]int, len(table[0]))
for _, line := range table {
for i, val := range line {
if len(val) > columnLengths[i] {
columnLengths[i] = len(val)
}
}
}
var lineLength int
for _, c := range columnLengths {
lineLength += c + 3 // +3 for 3 additional characters before and after each field: "| %s "
}
lineLength += 1 // +1 for the last "|" in the line
for i, line := range table {
if i == 0 { // table header
fmt.Printf("+%s+\n", strings.Repeat("-", lineLength-2)) // lineLength-2 because of "+" as first and last character
}
for j, val := range line {
fmt.Printf("| %-*s ", columnLengths[j], val)
if j == len(line)-1 {
fmt.Printf("|\n")
}
}
if i == 0 || i == len(table)-1 { // table header or last line
fmt.Printf("+%s+\n", strings.Repeat("-", lineLength-2)) // lineLength-2 because of "+" as first and last character
}
}
}
func main() {
var table = [][]string{
{"vegetables", "fruits", "rank"},
{"potato", "strawberry", "1"},
{"lettuce", "raspberry", "2"},
{"carrot", "apple", "3"},
{"broccoli", "pomegranate", "4"},
}
printTable(table)
}

输出

+---------------------------------+
| vegetables | fruits | rank |
+---------------------------------+
| potato | strawberry | 1 |
| lettuce | raspberry | 2 |
| carrot | apple | 3 |
| broccoli | pomegranate | 4 |
+---------------------------------+

大部分的代码都是用来计算要打印的字符数的,如果你仔细地跟着它走,你就不难理解。我们最感兴趣的是这一行。

fmt.Printf("| %-*s ", columnLengths[j], val)

格式中的星号* ,指定padding的大小应该作为一个参数给到 fmt.Printf()函数的参数。在这种情况下,填充物是columnLengths[j]

重新排列参数

也可以重新排列格式字符串中的参数。下面的例子表明,通过在格式化占位符前面的方括号中添加数值,我们可以控制每个参数出现的顺序。

fmt.Printf("| %-[2]*[1]s ", val, columnLengths[j])

如果我们在字符串格式中有重复的值,[n] 符号就特别有用。在这种情况下,它们只能作为一个参数被赋予一次。

fmt.Printf("I'm a %[1]s, a great %[1]s\n", "programmer")

带有可变大小填充物的总结性例子:

格式描述
"%s"标准字符串格式
"%*s"在左边用可变数量的空格对字符串进行填充(右对齐的字符串)
"%-*s"在右边用可变数量的空格填充字符串(左对齐字符串)。
"%-[2]*[1]s"同上,但第一个参数是字符串,第二个参数是填充大小

在对齐的列中打印文本,使用 text/tabwriter

Go标准库还包括一个有用的 text/tabwriter包,它可以从用\t 字符分隔的字符串中创建正确对齐的表格。这种类型的格式化对于创建打印表格数据的命令行应用程序特别有用。在下面的例子中,我们初始化 tabwriter.Writer将输出设置为标准输出 os.Stdout\t 作为填充字符,4 作为制表符的宽度。为了便于打印,行单元格应该被串联成一个由制表符分隔的字符串。我们使用 strings.Join()函数来完成。该 tabwriter.Writer要求每一列单元格都以制表符结尾,所以我们还为该行的最后一个单元格添加\t

package main
import (
"fmt"
"os"
"strings"
"text/tabwriter"
)
func printTable(table [][]string) {
writer := tabwriter.NewWriter(os.Stdout, 0, 4, 0, '\t', 0)
for _, line := range table {
fmt.Fprintln(writer, strings.Join(line, "\t")+"\t")
}
writer.Flush()
}
func main() {
var table = [][]string{
{"vegetables", "fruits", "rank"},
{"potato", "strawberry", "1"},
{"lettuce", "raspberry", "2"},
{"carrot", "apple", "3"},
{"broccoli", "pomegranate", "4"},
}
printTable(table)
}

输出

vegetables fruits rank
potato strawberry 1
lettuce raspberry 2
carrot apple 3
broccoli pomegranate 4