访问字符串的元素作为字节执行转换?

时间:2017-06-11 19:31:21

标签: string go type-conversion rune

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

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

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

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

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

2 个答案:

答案 0 :(得分:3)

Go中的

string值存储文本的UTF-8编码字节,而不是其字符或rune

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

for ... range上使用string时,会迭代rune的{​​{1}},而不是其字节!

因此,如果您要迭代string(字符),则必须使用runes但不能转换为for ... range,因为第一个表单不适用于{{ 1}}包含多(UTF-8)字节字符的值。 []byte上的spec allows youstring,第一个迭代值将是当前字符的字节索引,第二个值将是{{1}类型的当前字符值1}}(这是for ... range的别名):

  

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

简单示例:

string

输出(在Go Playground上尝试):

rune

必须为您阅读博文:

The Go Blog: Strings, bytes, runes and characters in Go

注意:如果您必须遍历int32字节(而不是其字符),请使用转换后的s := "Hi 世界" for i, c := range s { fmt.Printf("Char pos: %d, Char: %c\n", i, c) } Char pos: 0, Char: H Char pos: 1, Char: i Char pos: 2, Char: Char pos: 3, Char: 世 Char pos: 6, Char: 界 。第二个例子没有制作副本,它被优化了。有关详细信息,请参阅golang: []byte(string) vs []byte(*string)

答案 1 :(得分: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已经必须逐个字符地进行工作,而不是逐字节。