字符串切片是否执行基础数据的复制?

时间:2018-09-18 23:07:10

标签: string go utf-8 slice

我正在尝试使用utf8库从utf-8 string中有效地计算符文。此示例是否最佳,因为它不复制基础数据?
https://golang.org/pkg/unicode/utf8/#example_DecodeRuneInString

func main() {
    str := "Hello, 世界" // let's assume a runtime-provided string
    for len(str) > 0 {
        r, size := utf8.DecodeRuneInString(str)
        fmt.Printf("%c %v\n", r, size)
        str = str[size:] // performs copy?
    }
}

我在(不安全的)反射库中发现了StringHeader。这是Go中string的确切结构吗?如果是这样,可以想象切片字符串只会更新Data或完全分配一个新的StringHeader

type StringHeader struct {
        Data uintptr
        Len  int
}

奖金:在哪里可以找到执行string切片的代码,以便自己查看?有这些吗?
https://golang.org/src/runtime/slice.go
https://golang.org/src/runtime/string.go

related SO answer建议将运行时字符串从string转换为[]byte时产生一个副本。

1 个答案:

答案 0 :(得分:4)

切片字符串

  

字符串切片执行底层数据的复制吗?

不,不是。参见Russ Cox的这篇文章:

  

字符串在内存中表示为2字结构,其中包含指向字符串数据的指针和长度。因为字符串是不可变的,所以多个字符串共享相同的存储是安全的,因此切片s会导致新的2字结构,其指针和长度可能仍然不同,但仍指向相同的字节序列。这意味着切片无需分配或复制即可完成,从而使字符串切片的效率与传递显式索引一样高效。

-Go Data Structures

切片,性能和遍历符文

切片基本上是三件事:长度,容量和指向基础数组中某个位置的指针。

这样,切片本身并不是很大:int和一个指针(可能在实现细节中还有一些其他小东西)。因此,制作切片副本所需的分配非常小,并且与底层数组的大小无关。只需更新长度,容量和指针位置(例如,在以下内容的第2行上),就不需要新的分配:

foo := []int{3, 4, 5, 6}
foo = foo[1:]

相反,是当必须分配新的基础数组时,才会感觉到性能影响。

Go中的字符串是不可变的。因此,要更改字符串,您需要创建一个新字符串。但是,字符串与字节片密切相关,例如您可以使用

从字符串创建字节切片
foo := `here's my string`
fooBytes := []byte(foo)

我相信这将分配一个新的字节数组,因为:

  

字符串实际上是只读的字节片

根据Go Blog(请参阅Strings, bytes, runes and characters in Go)。通常,您可以使用切片来更改基础数组的内容,因此要从字符串中生成可用的字节切片,则必须进行复制以防止用户更改应该不变的内容。

您可以使用performance profilingbenchmarking来进一步了解程序的性能。

一旦有了字节片fooBytes,对其进行切片将不会分配新的数组,它只会分配一个很小的新片。切片字符串似乎也是如此。

请注意,您不需要使用utf8包来对utf8字符串中的单词进行计数,尽管您可以根据需要进行这种操作。 Go本机处理utf8。但是,如果要遍历字符,则不能将字符串表示为字节片,因为可能会有多字节字符。相反,您需要将其表示为一小段符文:

foo := `here's my string`
fooRunes := []rune(foo)

根据我的经验,这种将字符串转换为符文切片的操作非常快(在我做过的基准测试中很简单,但是可能会有分配)。现在,您可以遍历fooRunes来计数单词,而无需utf8包。另外,您可以跳过显式的[]rune(foo)转换,并通过在字符串上使用for ... range循环来隐式地进行转换,因为这些转换很特殊:

  相比之下,用于范围循环的

每次迭代都会解码一个UTF-8编码的符文。每次循环时,循环索引都是当前符文的起始位置(以字节为单位),代码点是其值。

-Strings, bytes, runes and characters in Go