string - 字符串 slice 是否执行基础数据的复制?

标签 string go utf-8 slice

我正在尝试使用 utf8 从 utf-8 string 有效地计算 rune 图书馆。这个例子是否是最佳的,因为它不复制底层数据?
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 的确切结构吗?如果是这样,可以想象对字符串进行 slice 只是更新 Data 或分配一个新的 StringHeader

type StringHeader struct {
        Data uintptr
        Len  int
}

Bonus:我在哪里可以找到执行 string slice 的代码,以便我自己查找?这些中的任何一个?
https://golang.org/src/runtime/slice.go
https://golang.org/src/runtime/string.go

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

最佳答案

slice 字符串

does slice of string perform copy of underlying data?

不,它没有。请参阅 Russ Cox 的这篇文章:

A string is represented in memory as a 2-word structure containing a pointer to the string data and a length. Because the string is immutable, it is safe for multiple strings to share the same storage, so slicing s results in a new 2-word structure with a potentially different pointer and length that still refers to the same byte sequence. This means that slicing can be done without allocation or copying, making string slices as efficient as passing around explicit indexes.

-- Go Data Structures

slice 、性能和 rune 迭代

一个 slice 基本上由三样东西组成:长度、容量和指向底层数组中某个位置的指针。

因此, slice 本身不是很大:整数和指针(可能是实现细节中的一些其他小东西)。因此复制 slice 所需的分配非常小,并且不依赖于底层数组的大小。当你简单地更新长度、容量和指针位置时,不需要新的分配,比如在第 2 行:

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

相反,当必须分配新的底层数组时,性能会受到影响。

Go 中的字符串是不可变的。因此,要更改字符串,您需要制作一个新字符串。然而,字符串与字节 slice 密切相关,例如您可以使用

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

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

a string is in effect a read-only slice of bytes

根据 Go 博客(参见 Strings, bytes, runes and characters in Go)。通常,您可以使用 slice 来更改底层数组的内容,因此要从字符串中生成可用的字节 slice ,您必须制作一个副本以防止用户更改本应不可变的内容。

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

一旦你有了你的 byte slice ,fooBytes,重新 slice 不会分配一个新的数组,它只是分配一个新的片,它很小。这似乎也是 slice 字符串的作用。

请注意,您不需要使用 utf8 程序包来计算 utf8 字符串中的单词数,但如果您愿意,也可以继续这样做。 Go 原生处理 utf8。但是,如果您想遍历字符,则不能将字符串表示为 byte slice 段,因为您可能有多字节字符。相反,您需要将其表示为一段 rune :

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

根据我的经验,这种将字符串转换为一段 rune 的操作很快(在我完成的基准测试中微不足道,但可能有分配)。现在您可以遍历 fooRunes 来计算单词数,不需要 utf8 包。或者,您可以跳过显式 []rune(foo) 转换并通过在字符串上使用 for ... range 循环隐式执行此操作,因为它们很特殊:

A for range loop, by contrast, decodes one UTF-8-encoded rune on each iteration. Each time around the loop, the index of the loop is the starting position of the current rune, measured in bytes, and the code point is its value.

-- Strings, bytes, runes and characters in Go

关于string - 字符串 slice 是否执行基础数据的复制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52395730/

相关文章:

mysql - 确定 oracle 列数据是否包含超出 U+0000 和 U+FFFF 范围的任何 unicode 字符

c++ - 在 sfml 中使用 UTF8

c++ - 在字符串中搜索字符

string - 如何按字典顺序对字符串进行排序?

java - 如何使用bufferedreader在java中接受字符串数组

windows - 使用原生 Golang API 在 Windows 上添加图标托盘

go - 如何使用 WaitGroup 确保 goroutines 在 for 循环中完成?

sql - MySQL:为什么使用 UTF-8

python - 根据名字、姓氏创建电子邮件地址

go - 如何使用 oauth2 在 Go 中实现隐式授权