根据https://blog.golang.org/strings和我的测试,看起来当我们range
是一个字符串时,我们得到的字符是rune
类型,但是如果我们得到它str[index]
,它们就会是byte
类型,这是为什么呢?
3 回答
关于为什么以这种方式定义语言的快速简单的答案。
想想符文是什么。Arune
表示一个 Unicode 代码点,它可以由多个字节组成,并且根据编码也有不同的表示。
mystring[i]
现在想想如果返回 arune
而不是 a会意味着什么byte
。由于不扫描字符串就无法知道每个符文的长度,因此该操作需要每次扫描整个字符串,从而使类似数组的访问需要 O(n) 而不是 O(1)。
如果mystring[i]
每次都扫描整个字符串,这对于语言的用户来说是非常违反直觉的,对于语言开发人员来说也更加复杂。这就是为什么大多数编程语言(如 Go、Rust、Python)区分 Unicode 字符和字节,有时只支持字节索引。
从一开始迭代时,一次访问一个字符串rune
要简单得多,例如使用range
. 可以扫描连续的字节并将它们组合在一起,直到它们形成一个可以返回为 的有效 Unicode 字符rune
,然后转到下一个。
首先,为什么是因为这就是语言的定义方式。String 类型告诉我们:
字符串值是一个(可能为空的)字节序列。字节数称为字符串的长度,永远不会是负数。字符串是不可变的:一旦创建,就不可能更改字符串的内容。
和:
可以通过整数索引 0 到 len(s)-1 访问字符串的字节。
同时,range
是一个可以插入到for
语句中的子句,规范说:
"range" 子句中右边的表达式称为范围表达式,可以是... [a] string ...
和:
- 对于字符串值,“range”子句从字节索引 0 开始迭代字符串中的 Unicode 代码点。在连续迭代中,索引值将是连续 UTF-8 编码代码点的第一个字节的索引字符串和 type 的第二个值
rune
将是相应代码点的值。如果迭代遇到无效的 UTF-8 序列,则第二个值将是0xFFFD
Unicode 替换字符,并且下一次迭代将在字符串中前进一个字节。
如果你想知道为什么语言是这样定义的,你真的必须问定义者自己。但是,请注意,如果for
仅在字节上进行范围,则需要构建自己的更高级的循环来范围在符文上。鉴于for ... range
确实可以通过符文工作,如果您想通过字符串中的字节来工作s
,您可以编写:
for i := 0; i < len(s); i++ {
...
}
并轻松访问s[i]
循环内部。你也可以写:
for i, b := range []byte(s) {
}
并访问循环内的索引i
和字节。b
(从 string 转换为[]byte
,或反之亦然,可能需要一个副本,因为[]byte
可以修改。但是,在这种情况下,range
不会修改它,编译器可以优化掉副本。请参阅下面的 icza 评论或对golang 的此答案:[ ]byte(string) vs []byte(*string) .) 所以你并没有失去任何能力,只是可能有点简洁。
只是让你知道。如果您想使用经典的 for循环遍历 astring
并使用运算符[]
来获取rune
,您可以执行以下操作:
{
rstr := []rune(MyString)
for idx := 0; idx < len(rstr); idx++ {
// code before...
currentRune := rstr[idx]
_ = currentRune // to avoid unused error
// code after...
}
}