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


381

有没有一种简单的方法可以在Go中格式化字符串而不打印字符串?

我可以:

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

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

我也可以做类似的事情:

s := "foo: " + bar

但这在格式字符串很复杂时很难阅读,而在一个或多个部分不是字符串而必须首先转换的时候(例如,

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

有没有更简单的方法可以做到这一点?

Answers:


465

Sprintf是您想要的。

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

您还可以在“ 错误”示例中看到它在使用中,作为“ A Tour of Go”的一部分。

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

6
%之后的字母重要吗?可能是%y和%q吗?或%y和%y
Filip Bartuzi

17
字母确实很重要,称为动词,基本上它使Sprintf知道变量的类型,因此,如果接收到65并且动词为%d,它将打印数字65,但是如果动词为%c,则将打印字符'一个'。请参阅:golang.org/pkg/fmt/#hdr-Printing
redsalt

2
为什么叫Sprintf?S代表字符串,f代表格式?如果功能没有输出到屏幕,则打印是功能名称的一部分是很奇怪的。这使我困惑了一段时间……
jcollum

194

1.简单的字符串

对于“简单”字符串(通常适合一行),最简单的解决方案是使用fmt.Sprintf()和朋友(fmt.Sprint()fmt.Sprintln())。这些类似于不带首S字母的功能,但是这些Sxxx()变体将结果返回,string而不是将其打印到标准输出。

例如:

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

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

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

提示:如果只想连接不同类型的值,则可能不需要Sprintf()Sprint()这样完全自动使用(需要格式字符串)。请参阅以下示例:

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

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

Go Playground上尝试一下。

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

如果您要创建的字符串比较复杂(例如,多行电子邮件),fmt.Sprintf()那么可读性和效率就会降低(尤其是您必须多次执行此操作时)。

为此,标准库提供了软件包text/templatehtml/template。这些软件包实现了数据驱动的模板以生成文本输出。html/template用于生成可防止代码注入的HTML输出。它提供了与包相同的接口,text/template并且应text/template在输出为HTML时代替其使用。

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

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

例:

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

Hi [name]!

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

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

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

const emailTmpl = `Hi {{.Name}}!

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

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

并提供如下数据来执行它:

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

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

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

这将产生预期的输出:

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.Bufferstrings.Builder。用法非常相似:

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

Go Playground上尝试一下。

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

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

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


2

在您的情况下,您需要使用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)

您的输出将是:

早上好,我是约翰,过去二十年来我一直住在这里。


0

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


0

我们可以自定义一个新的String类型通过define new TypeFormat支持。

package main

import (
    "fmt"
    "text/template"
    "strings"
)

type String string
func (s String) Format(data map[string]interface{}) (out string, err error) {
    t := template.Must(template.New("").Parse(string(s)))
    builder := &strings.Builder{}
    if err = t.Execute(builder, data); err != nil {
        return
    }
    out = builder.String()
    return
}


func main() {
    const tmpl = `Hi {{.Name}}!  {{range $i, $r := .Roles}}{{if $i}}, {{end}}{{.}}{{end}}`
    data := map[string]interface{}{
        "Name":     "Bob",
        "Roles":    []string{"dbteam", "uiteam", "tester"},
    }

    s ,_:= String(tmpl).Format(data)
    fmt.Println(s)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.