15

根据https://blog.golang.org/strings和我的测试,看起来当我们range是一个字符串时,我们得到的字符是rune类型,但是如果我们得到它str[index],它们就会是byte类型,这是为什么呢?

4

3 回答 3

13

关于为什么以这种方式定义语言的快速简单的答案。

想想符文是什么。Arune表示一个 Unicode 代码点,它可以由多个字节组成,并且根据编码也有不同的表示。

mystring[i]现在想想如果返回 arune而不是 a会意味着什么byte。由于不扫描字符串就无法知道每个符文的长度,因此该操作需要每次扫描整个字符串,从而使类似数组的访问需要 O(n) 而不是 O(1)。

如果mystring[i]每次都扫描整个字符串,这对于语言的用户来说是非常违反直觉的,对于语言开发人员来说也更加复杂。这就是为什么大多数编程语言(如 Go、Rust、Python)区分 Unicode 字符和字节,有时只支持字节索引。

从一开始迭代时,一次访问一个字符串rune要简单得多,例如使用range. 可以扫描连续的字节并将它们组合在一起,直到它们形成一个可以返回为 的有效 Unicode 字符rune,然后转到下一个。

于 2019-10-31T01:48:42.607 回答
12

首先,为什么是因为这就是语言的定义方式String 类型告诉我们:

字符串值是一个(可能为空的)字节序列。字节数称为字符串的长度,永远不会是负数。字符串是不可变的:一旦创建,就不可能更改字符串的内容。

和:

可以通过整数索引 0 到 len(s)-1 访问字符串的字节。

同时,range是一个可以插入到for语句中的子句,规范说:

"range" 子句中右边的表达式称为范围表达式,可以是... [a] string ...

和:

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

如果你想知道为什么语言是这样定义的,你真的必须问定义者自己。但是,请注意,如果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) .) 所以你并没有失去任何能力,只是可能有点简洁

于 2019-10-31T01:38:18.417 回答
0

只是让你知道。如果您想使用经典的 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...
  }
}
于 2021-11-24T13:28:04.167 回答