符文与字符串范围内的字节

Rune vs byte ranging over string

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

到第一层,为什么是因为how the language is defined. The String type告诉我们:

A string value is a (possibly empty) sequence of bytes. The number of bytes is called the length of the string and is never negative. Strings are immutable: once created, it is impossible to change the contents of a string.

和:

A string's bytes can be accessed by integer indices 0 through len(s)-1.

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

The expression on the right in the "range" clause is called the range expression, which may be ... [a] string ...

和:

  1. For a string value, the "range" clause iterates over the Unicode code points in the string starting at byte index 0. On successive iterations, the index value will be the index of the first byte of successive UTF-8-encoded code points in the string, and the second value, of type rune, will be the value of the corresponding code point. If the iteration encounters an invalid UTF-8 sequence, the second value will be 0xFFFD, the Unicode replacement character, and the next iteration will advance a single byte in the string.

如果你想知道为什么语言是这样定义的,你真的必须问定义者自己。但是,请注意,如果 for 仅在字节范围内排列,则您需要构建自己的更高级的循环以在符文范围内排列。鉴于 for ... range 确实 通过符文工作,如果您 想要 通过字符串 s 中的字节工作,你可以写:

for i := 0; i < len(s); i++ {
    ...
}

并在循环内轻松访问 s[i]。你也可以这样写:

for i, b := range []byte(s) {
}

并在循环内访问索引 i 和字节 b。 (从字符串到 []byte 的转换,反之亦然,可能需要一个副本,因为 []byte 可以修改。但在这种情况下,range 不会修改它,编译器可以优化离开副本。请参阅 or to .) So you have not lost any ability, just perhaps a smidgen 的简洁性。

只是简单快速地回答为什么要这样定义语言。

想想符文是什么。一个rune表示一个Unicode码位,可以由多个字节组成,根据编码的不同也有不同的表示。

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

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

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

只是让你知道。如果您想使用 经典 for 循环遍历 string 并使用运算符 [] 获取 rune,您可以执行以下操作:

{
  rstr := []rune(MyString)
  for idx := 0; idx < len(rstr); idx++ {
    // code before...
    currentRune := rstr[idx]
    _ = currentRune // to avoid unused error
    // code after...
  }
}