【问题标题】:How to ignore extra fields for fmt.Sprintf如何忽略 fmt.Sprintf 的额外字段
【发布时间】:2016-01-27 16:45:15
【问题描述】:

我有一个 Golang 程序,它从命令行读取字符串参数并将其传递给 fmt.Sprintf 函数。假设 tmp_str 是命令行的目标字符串。

package main

import "fmt"

func main() {
    tmp_str := "hello %s"
    str := fmt.Sprintf(tmp_str, "world")
    fmt.Println(str)
}

在某些情况下,程序会传递一个完整的字符串,如“Hello Friends”,而不是字符串模板。程序会恐慌并返回:

你好朋友%!(EXTRA string=world)

那么,如何忽略 fmt.Sprintf 的额外字段?

【问题讨论】:

  • 我不确定 Go 的 fmt 库是否存在与 C 相同的安全问题,但为了安全起见,您永远不应该使用来自不受信任来源的格式字符串。在 C 中,这会导致信息泄漏,例如打印出敏感的堆栈数据,甚至数据覆盖和代码执行。
  • @ZanLynx 感谢更新。我只是简化了这个问题。该程序肯定不会暴露给其他人。
  • @Zan Lynx Go 的 fmt 库和常规 Go 代码的其他地方没有这样的安全问题。这并不意味着随意处理格式字符串是个好主意。

标签: go


【解决方案1】:

是的,你可以这样做,方法是将传递给可变参数 Sprintf 函数的参数切片:

func TruncatingSprintf(str string, args ...interface{}) (string, error) {
    n := strings.Count(str, "%s")
    if n > len(args) {
        return "", errors.New("Unexpected string:" + str)
    }
    return fmt.Sprintf(str, args[:n]...), nil
}

func main() {
    tmp_str := "hello %s %s %s"         // don't hesitate to add many %s here
    str, err := TruncatingSprintf(tmp_str, "world") // or many arguments here
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(str)
}

Demonstration 1

Demonstration 2(即使 %s 多于参数,也会输出不同的版本)

但是您通常不使用动态格式的字符串,这不安全,如果您想接受 any 字符串,您还应该调整此代码以不阻塞%%s。如果您冒险到这里,那么您可能应该看看templates(它可以让您使用命名字符串,因此丢失的字符串不必是最后一个)。

【讨论】:

  • 很好的答案。添加了一些 OP 可以扩展的错误处理:play.golang.org/p/NSFFzgLC7B
  • @william.taylor.09 好主意。我接受了您的错误处理并将其添加到答案中,这样没有人会错过它。但请注意,拥有比参数更多的 %s 可能被认为是OK(只有一些“MISSING”)
  • 添加索引占位符有帮助:fmt.Sprintf("Hello"+"%[2]s", "World", "")
【解决方案2】:

我同意Volker's answer,但你可以检查你的输入字符串:

package main

import (
    "fmt"
    "strings"
)

func main() {
    tmp_str := "hello %s"

    res := tmp_str
    if strings.Count(tmp_str, "%s") == 1 {
        res = fmt.Sprintf(tmp_str, "world")
    }
    fmt.Println(res)
}

【讨论】:

  • 感谢您的回复。我对 Golang 有点陌生。我觉得你的回答很有启发性!
【解决方案3】:

在这种特殊情况下,您可以要求命令行用户始终提供 %s 动词,并说明他们可以将字符串截断为零长度:

Hello Friends%.0s

甚至更短:

Hello Friends%.s

输出很简单:

朋友们好

【讨论】:

  • 在我的特殊情况下,这是完美的。简单明了。
  • 这对我也很有效。我还注意到您可以使用“%.T”来忽略任何数据类型(不仅仅是字符串!)。例如,fmt.Printf("arg1=%s, arg2=%.T, arg3=%.T, arg4=%.T, arg=%s", "arg1", 2.123, true, "arg4", "arg5") => "arg1=arg1, arg2=, arg3=, arg4=, arg=arg5"(参见:play.golang.org/p/m0ahZ7ofAH_fgolang.org/pkg/fmt)。但是,使用这种方法时,会出现“Printf format %.T has unrecognized flag ”。 Go vet 的错误,我还没有解释。谢谢斯坦和罗香波! :)
【解决方案4】:

我用这个(可能可以扩展)

Sprintf("Hello"+"%[2]s", "World", "")
Sprintf("Hello %s"+"%[2]s", "World", "")

【讨论】:

    【解决方案5】:

    你不能这样做。

    你必须找到不同的解决方案。

    【讨论】:

    • 这是评论,不是答案。为什么会被赞成?
    • (原始)问题是关于如何让fmt.Printf 忽略额外的参数,这是无法做到的。不同的解决方案是预处理您的参数,但这是编程 101,与 fmt.Printf 无关。
    猜你喜欢
    • 2016-11-10
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 2015-06-12
    • 2022-10-18
    • 2019-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多