在Python中,你可以这样做:
"File {file} had error {error}".format(file=myfile, error=err)
或者这个:
"File %(file)s had error %(error)s" % {"file": myfile, "error": err}
在Go中,最简单的选项是:
fmt.Sprintf("File %s had error %s", myfile, err)
,它不允许您交换格式字符串中的参数顺序,您需要为I18N执行此操作。 Go 包含template
包,其中包含以下内容:
package main
import (
"bytes"
"text/template"
"os"
)
func main() {
type Params struct {
File string
Error string
}
var msg bytes.Buffer
params := &Params{
File: "abc",
Error: "def",
}
tmpl, _ := template.New("errmsg").Parse("File {{.File}} has error {{.Error}}")
tmpl.Execute(&msg, params)
msg.WriteTo(os.Stdout)
}
这似乎是一个很长的路要走错误信息。是否有更合理的选项允许我提供独立于订单的字符串参数?
答案 0 :(得分:29)
strings.Replacer
使用strings.Replacer
,实现您想要的格式化程序非常简单紧凑。
func main() {
file, err := "/data/test.txt", "file not found"
log("File {file} had error {error}", "{file}", file, "{error}", err)
}
func log(format string, args ...string) {
r := strings.NewReplacer(args...)
fmt.Println(r.Replace(format))
}
输出(在Go Playground上尝试):
File /data/test.txt had error file not found
我们可以通过在log()
函数中自动添加括号到参数名称来使用起来更加愉快:
func main() {
file, err := "/data/test.txt", "file not found"
log2("File {file} had error {error}", "file", file, "error", err)
}
func log2(format string, args ...string) {
for i, v := range args {
if i%2 == 0 {
args[i] = "{" + v + "}"
}
}
r := strings.NewReplacer(args...)
fmt.Println(r.Replace(format))
}
输出(在Go Playground上尝试):
File /data/test.txt had error file not found
是的,你可以说这只接受string
参数值。这是真的。稍微改善一点,这是不正确的:
func main() {
file, err := "/data/test.txt", 666
log3("File {file} had error {error}", "file", file, "error", err)
}
func log3(format string, args ...interface{}) {
args2 := make([]string, len(args))
for i, v := range args {
if i%2 == 0 {
args2[i] = fmt.Sprintf("{%v}", v)
} else {
args2[i] = fmt.Sprint(v)
}
}
r := strings.NewReplacer(args2...)
fmt.Println(r.Replace(format))
}
输出(在Go Playground上尝试):
File /data/test.txt had error 666
此变体接受params为map[string]interface{}
并将结果作为string
返回:
type P map[string]interface{}
func main() {
file, err := "/data/test.txt", 666
s := log33("File {file} had error {error}", P{"file": file, "error": err})
fmt.Println(s)
}
func log33(format string, p P) string {
args, i := make([]string, len(p)*2), 0
for k, v := range p {
args[i] = "{" + k + "}"
args[i+1] = fmt.Sprint(v)
i += 2
}
return strings.NewReplacer(args...).Replace(format)
}
在Go Playground上尝试。
text/template
您的模板解决方案或提案也过于冗长。它可以写成紧凑的(省略错误检查):
type P map[string]interface{}
func main() {
file, err := "/data/test.txt", 666
log4("File {{.file}} has error {{.error}}", P{"file": file, "error": err})
}
func log4(format string, p P) {
t := template.Must(template.New("").Parse(format))
t.Execute(os.Stdout, p)
}
输出(在Go Playground上尝试):
File /data/test.txt has error 666
如果您想要返回string
(而不是将其打印到标准输出),您可以这样做(在Go Playground上尝试):
func log5(format string, p P) string {
b := &bytes.Buffer{}
template.Must(template.New("").Parse(format)).Execute(b, p)
return b.String()
}
在另一个答案中已经提到过,但要完成它,要知道相同的显式参数索引可以使用任意次数,从而导致多次替换相同的参数。请在此问题中详细了解此问题:Replace all variables in Sprintf with same variable
答案 1 :(得分:13)
我不知道命名参数的简单方法,但您可以使用显式参数索引轻松更改参数的顺序:
来自docs:
在Printf,Sprintf和Fprintf中,默认行为是每个格式化动词格式化调用中传递的连续参数。但是,紧接在动词之前的符号[n]表示要格式化第n个单索引参数。 ' *'之前的相同符号对于宽度或精度,选择保存该值的参数索引。在处理括号内的表达式[n]之后,除非另有指示,否则后续动词将使用参数n + 1,n + 2等。
然后你可以,即:
fmt.Printf("File %[2]s had error %[1]s", err, myfile)
答案 2 :(得分:3)
该参数也可以是一个映射,因此如果您不介意每次使用它时解析每个错误格式,以下函数都可以使用:
package main
import (
"bytes"
"text/template"
"fmt"
)
func msg(fmt string, args map[string]interface{}) (str string) {
var msg bytes.Buffer
tmpl, err := template.New("errmsg").Parse(fmt)
if err != nil {
return fmt
}
tmpl.Execute(&msg, args)
return msg.String()
}
func main() {
fmt.Printf(msg("File {{.File}} has error {{.Error}}\n", map[string]interface{} {
"File": "abc",
"Error": "def",
}))
}
它仍然比我想要的有点啰嗦,但我认为它比其他一些选项更好。您可以将map[string]interface{}
转换为本地类型,然后将其进一步缩小为:
type P map[string]interface{}
fmt.Printf(msg("File {{.File}} has error {{.Error}}\n", P{
"File": "abc",
"Error": "def",
}))
答案 3 :(得分:1)
唉,Go中没有内置函数用于指定参数的字符串插值(尚未)。但是你并不是唯一受苦的人:)有些软件包应该存在,例如:https://github.com/imkira/go-interpol。或者,如果你喜欢冒险,你可以自己写一个这样的帮手,因为这个概念实际上非常简单。
干杯, 丹尼斯
答案 4 :(得分:0)
您可以非常接近这种甜美的 python格式化体验:
message := FormatString("File {file} had error {error}", Items{"file"=myfile, "error"=err})
在代码中的某处声明以下内容:
type Items map[string]interface{}
func FormatString(template string, items Items) string {
for key, value := range items {
template = strings.ReplaceAll(template, fmt.Sprintf("{%v}", key), fmt.Sprintf("%v", value))
}
return template
}
看到具有这种简单签名的开发经验的潜力,我很想吸引并上传了一个名为format
的go包。
package main
import (
"fmt"
"github.com/jossef/format"
)
func main() {
formattedString := format.String(`hello "{name}". is lizard? {isLizard}`, format.Items{"name": "Mr Dude", "isLizard": false})
fmt.Println(formattedString)
}
答案 5 :(得分:0)
您可以尝试使用Go Formatter库,该库实现替换字段,并用大括号dependencies {
modules {
module("my.artifact.group:bar") {
replacedBy(project(":bar-project:bar"), "using subproject-dependencies for now")
}
}
}
格式的字符串包围,类似于Python格式。
工作代码示例Go Playground:
{}
输出:
package main
import (
"fmt"
"gitlab.com/tymonx/go-formatter/formatter"
)
func main() {
formatted, err := formatter.Format("Named placeholders {file}:{line}:{function}():", formatter.Named{
"line": 3,
"function": "func1",
"file": "dir/file",
})
if err != nil {
panic(err)
}
fmt.Println(formatted)
}
答案 6 :(得分:0)
text/template 很有趣。我在下面提供一些例子
echo "Please enter the start date in dd/Mmm/yyyy format: "
read x
echo "Please enter the end date in dd/Mmm/yyyy format: "
read y
cat access.log | grep -E 'x|y' | awk '{print $1}' | sort -n | uniq -c | sort -nr
func TestFString(t *testing.T) {
// Example 1
fs := &FString{}
fs.MustCompile(`Name: {{.Name}} Msg: {{.Msg}}`, nil)
fs.MustRender(map[string]interface{}{
"Name": "Carson",
"Msg": 123,
})
assert.Equal(t, "Name: Carson Msg: 123", fs.Data)
fs.Clear()
// Example 2 (with FuncMap)
funcMap := template.FuncMap{
"largest": func(slice []float32) float32 {
if len(slice) == 0 {
panic(errors.New("empty slice"))
}
max := slice[0]
for _, val := range slice[1:] {
if val > max {
max = val
}
}
return max
},
"sayHello": func() string {
return "Hello"
},
}
fs.MustCompile("{{- if gt .Age 80 -}} Old {{else}} Young {{- end -}}"+ // "-" is for remove empty space
"{{ sayHello }} {{largest .Numbers}}", // Use the function which you created.
funcMap)
fs.MustRender(Context{
"Age": 90,
"Numbers": []float32{3, 9, 13.9, 2.1, 7},
})
assert.Equal(t, "Old Hello 13.9", fs.Data)
}
答案 7 :(得分:0)
不是使用 template.New
,您必须在其中提供模板名称,而是
可以实例化一个模板指针:
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() {
params := struct{File, Error string}{"abc", "def"}
println(format("File {{.File}} has error {{.Error}}", params))
}