与Go中的Python string.format等效?

与Go中的Python string.format等效?,python,string,go,Python,String,Go,在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) 这不允许您交换格式字符串中参数的顺序,这是您需要做的。

在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)
这不允许您交换格式字符串中参数的顺序,这是您需要做的。Go确实有
模板
包,它需要如下内容:

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)
}

这似乎是一个很长的路要走的错误消息。是否有更合理的选项允许我独立于顺序给出字符串参数?

我不知道命名参数的任何简单方法,但您可以使用显式参数索引轻松更改参数的顺序:

发件人:

在Printf、Sprintf和Fprintf中,默认行为是每个格式化谓词格式化调用中传递的连续参数。但是,动词前面的符号[n]表示要格式化第n个索引参数。宽度或精度的“*”前面的相同符号选择保存该值的参数索引。在处理括号内的表达式[n]后,后续动词将使用参数n+1、n+2等,除非另有指示

然后你可以,即:

fmt.Printf("File %[2]s had error %[1]s", err, myfile)

该参数也可以是一个映射,因此,如果您不介意每次使用时解析每个错误格式,则以下函数将起作用:

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]接口{}
转换为本地类型,并将其进一步简化为:

type P map[string]interface{}

fmt.Printf(msg("File {{.File}} has error {{.Error}}\n", P{
        "File": "abc",
        "Error": "def",
    }))
字符串。替换器

使用,实现你想要的格式化程序是非常简单和紧凑的

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))
}
输出(在上尝试):

我们可以通过在
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))
}
输出(在上尝试):

是的,您可以说它只接受
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))
}
输出(在上尝试):

此函数的一个变体,用于将参数接受为
map[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 for string interpolation中没有内置的函数和命名参数。但你不是唯一一个遭受痛苦的人:)有些包应该存在,例如:。或者,如果你喜欢冒险,你可以自己写这样一个助手,因为这个概念其实很简单

干杯,
Dennis

您可以非常接近那种甜美的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
}
  • 您可以尝试使用库来实现替换字段,该库由大括号
    {}
    格式字符串包围,类似于Python格式

    工作代码示例:

    主程序包
    进口(
    “fmt”
    “gitlab.com/tymonx/go formatter/formatter”
    )
    func main(){
    格式化,err:=formatter.Format(“命名占位符{file}:{line}:{function}():”,formatter.Named{
    "行":3,,
    “函数”:“func1”,
    “文件”:“目录/文件”,
    })
    如果错误!=零{
    恐慌(错误)
    }
    格式打印项次(格式化)
    }
    
    输出:

    Named placeholders dir/file:3:func1():
    
    这很有趣。下面我提供一些例子

    用法
    func TestFString(t*testing.t){
    //例1
    fs:=&FString{}
    fs.MustCompile(`Name:{{.Name}}Msg:{{{.Msg}}}`,nil)
    fs.MustRender(映射[字符串]接口{}{
    “姓名”:“卡森”,
    “Msg”:123,
    })
    assert.Equal(t,“Name:Carson Msg:123”,fs.Data)
    财政司司长
    //示例2(带FuncMap)
    funcMap:=模板。funcMap{
    “最大”:func(片[]浮点32)浮点32{
    如果len(slice)==0{
    恐慌(错误。新建(“空切片”))
    }
    最大值:=切片[0]
    对于u,val:=范围片[1:]{
    如果val>max{
    max=val
    }
    }
    返回最大值
    },
    “sayHello”:func()字符串{
    回复“你好”
    },
    }
    fs.MustCompile(“{-if gt.Age 80-}Old{{else}}Young{{-end-}}”+/-”用于删除空空间
    “{{sayHello}}{{maxist.Numbers}}}”,//使用您创建的函数。
    (地图)
    fs.MustRender(上下文){
    “年龄”:90岁,
    “数字”:[]32{3,9,13.9,2.1,7},
    })
    assert.Equal(t,“Old Hello 13.9”,fs.Data)
    }
    
    解放党
    包utils
    进口(
    “文本/模板”
    )
    类型上下文映射[字符串]接口{}
    类型FString结构{
    数据串
    template*template.template
    }
    func(fs*FString)必须编译(expr string,funcMap template.funcMap){
    fs.template=template.Must(template.New(“f-string”)。
    Funcs(funcMap)。
    解析(expr))
    }
    func(fs*FString)写入(b[]字节)(n int,err error){
    fs.Data+=字符串(b)
    返回len(b),无
    }
    func(fs*FString)呈现(上下文映射[string]接口{})错误{
    如果错误:=fs.template.Execute(fs,context);错误!=nil{
    返回错误
    }
    归零
    }
    func(fs*FString)MustRender(上下文){
    如果err:=fs.Render(上下文);err!=nil{
    恐慌(错误)
    }
    }
    func(fs*FString)Clear()字符串{
    //返回数据并清除它
    out:=fs.Data
    fs.Data=“”
    返回
    }
    

    重要的
    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)
    }
    
    File /data/test.txt has error 666
    
    func log5(format string, p P) string {
        b := &bytes.Buffer{}
        template.Must(template.New("").Parse(format)).Execute(b, p)
        return b.String()
    }
    
    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
    }
    
    Named placeholders dir/file:3:func1():