格式化Go字符串而不打印?

Format a Go string without printing?

是否有一种简单的方法来在Go中格式化字符串而不打印字符串?

我可以:

1
2
bar :="bar"
fmt.Printf("foo: %s", bar)

但我希望返回格式化的字符串而不是打印,以便我可以进一步操作它。

我也可以这样做:

1
s :="foo:" + bar

但是当格式字符串很复杂时很难读取,当一个或多个部分不是字符串并且必须首先转换时很麻烦,比如

1
2
i := 25
s :="foo:" + strconv.Itoa(i)

有更简单的方法吗?


Sprintf正是您的目标。

1
fmt.Sprintf("foo: %s", bar)

你也可以在Errors示例中看到它作为"A Tour of Go"的一部分使用。

1
return fmt.Sprintf("at %v, %s", e.When, e.What)


1.简单的字符串

对于"简单"字符串(通常适用于一行),最简单的解决方案是使用fmt.Sprintf()和朋友(fmt.Sprint()fmt.Sprintln())。这些类似于没有starter S字母的函数,但这些Sxxx()变量将结果返回为string,而不是将它们打印到标准输出。

例如:

1
s := fmt.Sprintf("Hi, my name is %s and I'm %d years old.","Bob", 23)

变量S将使用以下值初始化:

1
Hi, my name is Bob and I'm 23 years old.

提示:如果您只想连接不同类型的值,则可能不会自动使用Sprintf()(需要格式字符串),因为Sprint()正是如此。看这个例子:

1
2
i := 23
s := fmt.Sprint("[age:", i,"]") // s will be"[age:23]"

对于仅连接string,您还可以使用strings.Join(),您可以在其中指定自定义分隔符string(放置在要连接的字符串之间)。

在Go Playground尝试这些。

2.复杂的字符串(文件)

如果您尝试创建的字符串更复杂(例如,多行电子邮件消息),则fmt.Sprintf()会降低可读性并降低效率(特别是如果您必须多次执行此操作)。

为此,标准库提供包text/templatehtml/template。这些包实现了数据驱动的模板,用于生成文本输出。 html/template用于生成针对代码注入安全的HTML输出。它提供与package text/template相同的接口,只要输出为HTML,就应该使用它而不是text/template

使用template软件包基本上要求您以string值的形式提供静态模板(可能源自文件,在这种情况下,您只提供文件名)可能包含静态文本,以及在引擎处理模板并生成输出时处理和执行。

您可以提供在静态模板中包含/替换的参数,这些参数可以控制输出生成过程。这些参数的典型形式是可以嵌套的struct s和map值。

例:

例如,假设您要生成如下所示的电子邮件:

1
2
3
4
5
6
Hi [name]!

Your account is ready, your user name is: [user-name]

You have the following roles assigned:
[role#1], [role#2], ... [role#n]

要生成这样的电子邮件正文,您可以使用以下静态模板:

1
2
3
4
5
6
7
const emailTmpl = `Hi {{.Name}}!

Your account is ready, your user name is: {{.UserName}}

You have the following roles assigned:
{{range $i, $r := .Roles}}{{if ne $i 0}}, {{end}}{{.}}{{end}}
`

并提供这样的数据来执行它:

1
2
3
4
5
data := map[string]interface{}{
   "Name":    "Bob",
   "UserName":"bob92",
   "Roles":    []string{"dbteam","uiteam","tester"},
}

通常,模板的输出会写入io.Writer,因此如果您希望结果为string,请创建并写入bytes.Buffer(实现io.Writer)。执行模板并将结果作为string

1
2
3
4
5
6
t := template.Must(template.New("email").Parse(emailTmpl))
buf := &bytes.Buffer{}
if err := t.Execute(buf, data); err != nil {
    panic(err)
}
s := buf.String()

这将产生预期的输出:

1
2
3
4
5
6
Hi Bob!

Your account is ready, your user name is: bob92

You have the following roles assigned:
dbteam, uiteam, tester

在Go Playground尝试一下。

另请注意,自Go 1.10起,bytes.Buffer可以使用更新,更快,更专业的替代方案,即:strings.Builder。用法非常相似:

1
2
3
4
5
builder := &strings.Builder{}
if err := t.Execute(builder, data); err != nil {
    panic(err)
}
s := builder.String()

在Go Playground尝试这个。

注意:如果提供os.Stdout作为目标(也实现io.Writer),您还可以显示模板执行的结果:

1
2
3
4
t := template.Must(template.New("email").Parse(emailTmpl))
if err := t.Execute(os.Stdout, data); err != nil {
    panic(err)
}

这会将结果直接写入os.Stdout。在Go Playground尝试这个。


在您的情况下,您需要使用Sprintf()格式字符串。

func Sprintf(format string, a ...interface{}) string

Sprintf根据格式说明符进行格式化并返回结果字符串。

s := fmt.Sprintf("Good Morning, This is %s and I'm living here from last %d years","John", 20)

你的输出将是:

Good Morning, This is John and I'm living here from last 20 years.


fmt.SprintF函数返回一个字符串,您可以使用与fmt.PrintF相同的方式格式化字符串