符文 vs 字节范围在字符串上

use*_*520 12 go rune

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

Mar*_*lli 9

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

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

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

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

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


tor*_*rek 8

对于第一级,原因是因为这就是语言的定义方式。该字符串类型告诉我们:

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

和:

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

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

“范围”子句中右侧的表达式称为范围表达式,它可能是... [a] 字符串...

和:

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

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

for i := 0; i < len(s); i++ {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

for i, b := range []byte(s) {
}
Run Code Online (Sandbox Code Playgroud)

并访问循环内的索引i和字节b。(从字符串转换为[]byte,反之亦然,可能需要一个副本,因为[]byte可以修改。不过,在这种情况下,range不会修改它,编译器可以优化掉副本。请参阅下面的 icza 评论或对golang 的回答:[ ]字节(串)VS []字节(*字符串)。)所以,你还没有失去任何能力,只是也许点点简洁的。

  • 从“string”到“[]byte”的转换通常会生成一个副本,但在字符串的字节范围内(转换为“[]byte”)是一个例外,编译器会优化该副本。有关详细信息,请参阅 [golang: \[\]byte(string) 与 \[\]byte(*string)](/sf/ask/3042919911/#43470344 ) (2认同)