String 以字节形式访问字符串元素是否执行转换?
在Go中,要访问String 以字节形式访问字符串元素是否执行转换?,string,go,type-conversion,rune,String,Go,Type Conversion,Rune,在Go中,要访问字符串的元素,我们可以编写: str := "text" for i, c := range str { // str[i] is of type byte // c is of type rune } 访问str[i]时,Go是否执行从rune到byte的转换?我想答案是肯定的,但我不确定。如果是这样的话,那么以下哪种方法的性能更好?一种方法是否优于另一种方法(例如,在最佳实践方面) 或 Go中的string值存储文本的UTF-8编码字节,而不是其字符或runes s
字符串的元素
,我们可以编写:
str := "text"
for i, c := range str {
// str[i] is of type byte
// c is of type rune
}
访问str[i]
时,Go是否执行从rune
到byte
的转换?我想答案是肯定的,但我不确定。如果是这样的话,那么以下哪种方法的性能更好?一种方法是否优于另一种方法(例如,在最佳实践方面)
或
Go中的
string
值存储文本的UTF-8编码字节,而不是其字符或rune
s
string
索引其字节:str[i]
类型为byte
(或uint8
,其别名)。另外,字符串
实际上是一个只读的字节片(带有一些语法糖)。为字符串编制索引不需要将其转换为切片
当您将用于。。。范围
在字符串
上,它在字符串
的符文
上迭代,而不是其字节
因此,如果您想迭代符文
(字符),您必须使用来。。。范围
,但不转换为[]字节
,因为第一种形式不适用于包含多(UTF-8)字节字符的字符串
值。
要为…执行。。。范围
在字符串
值上,第一次迭代值将是当前字符的字节索引,第二次迭代值将是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)
}
输出(在上尝试):
必须阅读您的博客文章:
注意:如果必须迭代字符串的字节(而不是其字符),请使用作为。。。范围
与转换的字符串
类似,您的第二个示例没有复制,它已优化。有关详细信息,请参阅
以下哪种方法的性能更好
绝对不是这个
str := "large text"
str2 := []byte(str)
for _, s := range str2 {
// use s
}
字符串是不可变的<代码>[]字节
是可变的。这意味着[]字节(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
必须一个字符接一个字符,而不是一个字节接一个字节,所以性能没有差别。oops,我把它倒过来了关于[]字节/字符串的第一部分本身是不正确的。编译器在[]字节/字符串周围执行转义分析和一些额外的优化,以避免某些分配。如果用“-gcflags”-m-m”编译第一个代码段,您将看到这种情况不会分配,因为str2只在循环中使用,不会转义。@nussjustin是的。将其作为写入。。。范围[]字节(str)
使优化更为明显。通常,将字符串
转换为[]字节
会生成副本,但如果在中用于。。。范围
,则不会。编译器对它进行了优化。详情见此。
str := "large text"
str2 := []byte(str)
for _, s := range str2 {
// use s
}
str := "large text"
for _, s := range []byte(str) {
// use s
}
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