是否有一种简单的方法可以在Go中格式化字符串而不打印字符串?
我能做到:
bar := "bar"
fmt.Printf("foo: %s", bar)
但我希望返回格式化的字符串而不是打印,以便我可以进一步操作它。
我也可以这样做:
s := "foo: " + bar
但是当格式字符串很复杂时很难读取,当一个或多个部分不是字符串并且必须先转换时很麻烦,比如
i := 25
s := "foo: " + strconv.Itoa(i)
有更简单的方法吗?
答案 0 :(得分:381)
Sprintf正是您要找的。 p>
示例强>
fmt.Sprintf("foo: %s", bar)
您还可以在Errors example中看到它在“A Tour of Go”中使用。
return fmt.Sprintf("at %v, %s", e.When, e.What)
答案 1 :(得分:170)
对于"简单"字符串(通常是符合一行的字符串)最简单的解决方案是使用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]"
仅用于连接string
,您还可以使用strings.Join()
指定自定义分隔符string
(放置在要加入的字符串之间)。
在Go Playground上尝试这些。
如果您尝试创建的字符串更复杂(例如,多行电子邮件),则fmt.Sprintf()
会降低可读性并降低效率(特别是如果您必须多次执行此操作)。
为此,标准库提供了包text/template
和html/template
。这些包实现了数据驱动的模板,用于生成文本输出。 html/template
用于生成针对代码注入安全的HTML输出。它提供与包text/template
相同的接口,只要输出为HTML,就应该使用它而不是text/template
。
使用template
软件包基本上要求您以string
值的形式提供静态模板(可能源自文件,在这种情况下,您只提供文件名)包含静态文本,以及在引擎处理模板并生成输出时处理和执行的操作。
您可以提供静态模板中包含/替换的参数,这些参数可以控制输出生成过程。这些参数的典型形式是可以嵌套的struct
和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 ne $i 0}}, {{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.Buffer
可以使用更新,更快,更专业的替代方案:strings.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 :(得分:1)
在您的情况下,您需要使用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)
您的输出将是:
早上好,这是John和我在过去20年里住在这里。
答案 3 :(得分:1)
我们可以在define new Type
支持下通过Format
自定义新的字符串类型。
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)
}
答案 4 :(得分:1)
我已经创建了用于从模板进行字符串格式设置的go项目(它允许以 C#或Python 样式设置字符串格式,这是非常简单的情况下的第一个版本),可以在这里https://github.com/Wissance/stringFormatter
找到它它以以下方式工作:
func TestStrFormat(t *testing.T) {
strFormatResult, err := Format("Hello i am {0}, my age is {1} and i am waiting for {2}, because i am {0}!",
"Michael Ushakov (Evillord666)", "34", "\"Great Success\"")
assert.Nil(t, err)
assert.Equal(t, "Hello i am Michael Ushakov (Evillord666), my age is 34 and i am waiting for \"Great Success\", because i am Michael Ushakov (Evillord666)!", strFormatResult)
strFormatResult, err = Format("We are wondering if these values would be replaced : {5}, {4}, {0}", "one", "two", "three")
assert.Nil(t, err)
assert.Equal(t, "We are wondering if these values would be replaced : {5}, {4}, one", strFormatResult)
strFormatResult, err = Format("No args ... : {0}, {1}, {2}")
assert.Nil(t, err)
assert.Equal(t, "No args ... : {0}, {1}, {2}", strFormatResult)
}
func TestStrFormatComplex(t *testing.T) {
strFormatResult, err := FormatComplex("Hello {user} what are you doing here {app} ?", map[string]string{"user":"vpupkin", "app":"mn_console"})
assert.Nil(t, err)
assert.Equal(t, "Hello vpupkin what are you doing here mn_console ?", strFormatResult)
}
答案 5 :(得分:1)
我来到此页面的目的是专门寻找格式化错误字符串的方法。因此,如果有人需要帮助,可以使用fmt.Errorf()
函数。
方法签名为func Errorf(format string, a ...interface{}) error
。
它返回格式化的字符串作为满足error
接口的值。
您可以在文档https://golang.org/pkg/fmt/#Errorf中查找更多详细信息。
答案 6 :(得分:0)
fmt.SprintF 函数返回一个字符串,您可以按照与 fmt.PrintF
相同的方式设置字符串格式答案 7 :(得分:0)
尝试使用Sprintf()
;它不会打印输出,而是将其保存以备将来使用。
看看这个。
package main
import "fmt"
func main() {
address := "NYC"
fmt.Sprintf("I live in %v", address)
}
当你运行这段代码时,它不会输出任何东西。但是一旦您将 Sprintf()
分配给一个单独的变量,它就可以用于将来的目的。
package main
import "fmt"
func main() {
address := "NYC"
fmt.Sprintf("I live in %v", address)
var city = fmt.Sprintf("lives in %v", address)
fmt.Println("Michael",city)
}
答案 8 :(得分:0)
您可以使用内置的 template.New
而不是使用 new
template.Template:
package main
import (
"strings"
"text/template"
)
func format(s string, v interface{}) string {
t, b := new(template.Template), new(strings.Builder)
template.Must(t.Parse(s)).Execute(b, v)
return b.String()
}
func main() {
bar := "bar"
println(format("foo: {{.}}", bar))
i := 25
println(format("foo: {{.}}", i))
}