2

在 Go 中,要访问 a 的元素string,我们可以这样写:

str := "text"
for i, c := range str {
  // str[i] is of type byte
  // c is of type rune
}

访问时str[i],Go 是否执行从runeto的转换byte?我猜答案是肯定的,但我不确定。如果是这样,那么,以下哪一种方法在性能方面更好?一个优于另一个(例如,就最佳实践而言)?

str := "large text"
for i := range str {
  // use str[i]
}

或者

str := "large text"
str2 := []byte(str)
for _, s := range str2 {
  // use s
}
4

2 回答 2

3

stringGo 中的值存储文本的 UTF-8 编码字节,而不是其字符或runes。

索引astring索引它的字节:str[i]是类型byte(或者uint8,它的别名)。此外 astring实际上是一个只读的字节片(带有一些语法糖)。索引 astring不需要将其转换为切片。

当你for ... range在 a 上使用时string,它会迭代runes 的 s,而string不是它的字节!

因此,如果要遍历runes(字符),则必须使用 afor ... range但不转换为[]byte,因为第一种形式不适用于string包含多 (UTF-8) 字节字符的值。该规范允许您使用for ... range一个string值,第一次迭代值将是当前字符的字节索引,第二个值将是类型的当前字符值rune(这是 的别名int32):

对于字符串值,“range”子句从字节索引 0 开始迭代字符串中的 Unicode 代码点。在连续迭代中,索引值将是连续 UTF-8 编码代码点的第一个字节的索引rune 类型的字符串和第二个值将是相应代码点的值。如果迭代遇到无效的 UTF-8 序列,则第二个值将是 0xFFFD,即 Unicode 替换字符,并且下一次迭代将在字符串中前进一个字节。

简单的例子:

s := "Hi 世界"
for i, c := range s {
    fmt.Printf("Char pos: %d, Char: %c\n", i, c)
}

输出(在Go Playground上试试):

Char pos: 0, Char: H
Char pos: 1, Char: i
Char pos: 2, Char:  
Char pos: 3, Char: 世
Char pos: 6, Char: 界

必须为您阅读博客文章:

Go 博客:Go 中的字符串、字节、符文和字符


注意:如果您必须遍历a的字节string(而不是其字符),则使用 afor ... range和转换string后的第二个示例不会复制,它会被优化掉。有关详细信息,请参阅golang: []byte(string) vs []byte(*string)

于 2017-06-11T19:40:10.930 回答
1

以下哪一种方法在性能方面更好?

绝对不是这个。

str := "large text"
str2 := []byte(str)
for _, s := range str2 {
  // use s
}

字符串是不可变的。[]byte是可变的。这意味着[]byte(str)制作副本。所以上面将复制整个字符串。我发现不知道何时复制字符串成为大字符串性能问题的主要来源。

如果str2从不更改,编译器可能会优化掉副本。出于这个原因,最好像这样编写上面的代码,以确保字节数组永远不会改变。

str := "large text"
for _, s := range []byte(str) {
  // use s
}

这样就str2不可能在以后进行修改并破坏优化。

但这是一个坏主意,因为它会破坏任何多字节字符。见下文。


至于字节/符文转换,性能不是考虑因素,因为它们并不等效。c将是一个符文,str[i]将是一个字节。如果您的字符串包含多字节字符,则必须使用符文。

例如...

package main

import(
    "fmt"
)

func main() {
    str := "snow ☃ man"
    for i, c := range str {
        fmt.Printf("c:%c str[i]:%c\n", c, str[i])
    }
}

$ go run ~/tmp/test.go
c:s str[i]:s
c:n str[i]:n
c:o str[i]:o
c:w str[i]:w
c:  str[i]: 
c:☃ str[i]:â
c:  str[i]: 
c:m str[i]:m
c:a str[i]:a
c:n str[i]:n

请注意,使用str[i]会破坏多字节 Unicode 雪人,它仅包含多字节字符的第一个字节。

无论如何都没有性能差异,因为range str已经必须逐个字符地完成工作,而不是逐个字节。

于 2017-06-11T19:43:51.857 回答