【问题标题】:Rune vs byte ranging over string符文与字节范围在字符串上
【发布时间】:2019-10-31 00:43:25
【问题描述】:

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

【问题讨论】:

标签: go rune


【解决方案1】:

到第一层,为什么是因为那是how the language is definedString type 告诉我们:

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

和:

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

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

“range”子句右边的表达式称为range表达式,可以是... [a] string ...

和:

  1. 对于字符串值,“range”子句从字节索引 0 开始迭代字符串中的 Unicode 代码点。在连续迭代中,索引值将是连续 UTF-8 编码的第一个字节的索引字符串中的代码点,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。 (从字符串转换为[]byte,反之亦然,可能需要一个副本,因为[]byte 可以被修改。但在这种情况下,range 不会修改它,编译器可以优化掉副本。参见@ 987654324@ 或this answergolang: []byte(string) vs []byte(*string)。)所以你并没有失去任何能力,也许只是一个简洁的smidgen

【讨论】:

  • string[]byte 的转换通常会创建一个副本,但是跨越字符串字节(转换为[]byte)是一个例外,编译器会优化掉副本。详情见golang: []byte(string) vs []byte(*string)
  • @icza:啊,我只想到这样的情况,很明显结果没有(或不能)被修改。显然,转换为可修改的[]byte 通常需要一个副本。将解决上述问题。
【解决方案2】:

简单快速地回答一下为什么以这种方式定义语言。

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

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

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

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

【讨论】:

  • 快速跟进问题,如果我使用mystring[i] 扫描一个字符串,时间复杂度将是O(n^2),是吗?
  • 如果返回一个rune,是的,它将是 O(n^2)。由于它返回一个字节,因此复杂度为 O(n),其中 n 是字节数。
【解决方案3】:

只是让你知道。如果您想使用 classic 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...
  }
}

【讨论】:

    猜你喜欢
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 2012-04-21
    • 2013-06-04
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多