【问题标题】:go array initialization containing a string literalgo 包含字符串文字的数组初始化
【发布时间】:2018-12-24 02:47:00
【问题描述】:

我正在尝试将字节数组初始化为数字和字符串的混合。我已经设法使用一对追加来完成它,或者通过将字符串拆分为单独的字符,但为了便于阅读,有没有办法将它作为包含字符串的单个初始化程序来完成?

// method 1, with two appends
a := []byte{1, 2}
a = append(a, []byte("foo")...);
a = append(a, 3, 4);

// method 2, splitting the string into chars
b := []byte{1, 2, 'f', 'o', 'o', 3, 4)

【问题讨论】:

  • 这是正确的,但您应该使用一对 {} 而不是 { 和 )。

标签: string go append slice


【解决方案1】:

你在第一次尝试中所做的 3 行可能比任何单行代码都更具可读性:

(您可以尝试Go Playground上的所有示例。)

// Doing one-by-one:
a := []byte{1, 2}
a = append(a, []byte("foo")...)
a = append(a, 3, 4)
fmt.Println(a)

// Using individual chars:
a = []byte{1, 2, 'f', 'o', 'o', 3, 4}
fmt.Println(a)

// Using a single string literal:
a = []byte("\x01\x02foo\x03\x04")
fmt.Println(a)

// Using several "nested" appends:
a = append(append([]byte{1, 2}, []byte("foo")...), 3, 4)
fmt.Println(a)

除非你创建一个辅助函数:

func concat(s ...[]byte) []byte {
    var res []byte
    for _, v := range s {
        res = append(res, v...)
    }
    return res
}

然后使用它:

// With a utility function:
a = concat([]byte{1, 2}, []byte("foo"), []byte{3, 4})
fmt.Println(a)

// With a utility function, formatted differently:
a = concat(
    []byte{1, 2},
    []byte("foo"),
    []byte{3, 4},
)
fmt.Println(a)

您也可以使用单个键控复合文字和单个 copy() 调用来“插入”字符串:

// With keyed literal and copy:
a = []byte{1, 2, 5: 3, 4}
copy(a[2:], "foo")
fmt.Println(a)

我仍然不认为它更具可读性或价值。


连续优化

根据下面留下的 cmets,@EliasVanOotegem 对上面的解决方案进行了基准测试(在空切片上使用追加)并将其与所需字节切片的总容量相加并一次性分配该内存进行比较。后者的效率稍高一些(~20%),所以我将在下面包含该版本:

func concat(s ...[]byte) []byte {
    c := 0
    for _, v := range s {
        c += len(v)
    }
    res := make([]byte, 0, c) // allocate everything
    for _, v := range s {
        res = append(res, v...)
    }
    return res
}

我个人会使用以下优化版本,因为它使用内置 copy(),所以不需要切片头分配:

func concat(s ...[]byte) []byte {
    size := 0
    for _, v := range s {
        size += len(v)
    }
    res, i := make([]byte, size), 0
    for _, v := range s {
        i += copy(res[i:], v)
    }
    return res
}

【讨论】:

  • 只是好奇基准测试对concat 函数会说什么,如果您通过首先迭代[]byte 参数来指定一个上限,然后执行类似size := 0 和一个简单的for i := range s { size += len(s[i]) } 之类的操作并使用res := make([]byte, 0, size)。它将确保不会过度分配,并且实际上只进行了 1 次 alloc 调用
  • 好的,确实是微优化,但破解了一个小基准。 Concat2 是一个有多个分配(即没有指定上限):BenchmarkConcat-8 20000000 82.5 ns/op vs BenchmarkConcat2-8 20000000 109 ns/op
  • @EliasVanOotegem 我只是想展示使用实用函数的选项及其基本实现。如果我需要这个并且会经常使用它,显然我会优化它。在这种情况下,我只需预先分配大切片并使用copy() 而不是append(),这样您就可以摆脱切片头分配。
【解决方案2】:

FWIW:可以在附加调用中删除对[]byte(...) 的强制转换:

a := []byte{65, 66}
a = append(a, "foo"...)
a = append(a, 67, 68)

fmt.Printf("%s", a)

// outputs : ABfooCD

see it on play.golang.org

【讨论】:

    【解决方案3】:

    我想我误解了您最初尝试的内容。您可以将其编写为嵌入十六进制的字符串:

    c := []byte("\x01\x02foo\x03\x04")
    

    【讨论】:

      猜你喜欢
      • 2011-12-11
      • 1970-01-01
      • 1970-01-01
      • 2010-12-14
      • 1970-01-01
      • 2019-12-08
      • 2011-08-18
      • 2018-08-11
      相关资源
      最近更新 更多