在不使用 for ... range 的情况下访问字符串的随机符文元素

Access random rune element of string without using for ... range

我最近问了 问题,答案增加了我的理解,但它们并没有解决我遇到的实际问题。因此,我将尝试提出一个类似但不同的问题如下。

假设我想访问 string 的随机 rune 元素。一种方式是:

func RuneElement(str string, idx int) rune {
  var ret rune
  for i, c := range str {
    if i == idx {
      return c
    }
  }
  return ret // out of range -> proper handling is needed
}

如果我想多次调用这样的函数怎么办?我想我正在寻找的是 operator/function 之类的 str[i](return 是 byte),return rune 元素位于 i-第个位置。为什么可以使用 for ... range 而不是通过 str.At(i) 之类的函数访问此元素?

Go 中的

string 值存储文本的 UTF-8 编码字节序列。这是已经做出的设计决定,不会改变。

如果你想在任意索引处有效地从中获取 rune,你必须解码字节,对此你无能为力(for ... range 进行解码) .没有"shortcut"。所选择的表示形式并没有提供开箱即用的功能。

如果您必须经常/多次执行此操作,您应该更改您的输入而不是使用 string,而是使用 []rune,因为它是一个切片并且可以被有效地索引。 string 在 Go 中不是 []runestring 在 Go 中实际上是只读的 []byte (UTF-8)。期间.

如果您无法更改输入类型,您可以构建一个从 string 映射到其 []rune 的内部缓存:

var cache = map[string][]rune{}

func RuneAt(s string, idx int) rune {
    rs := cache[s]
    if rs == nil {
        rs = []rune(s)
        cache[s] = []rune(s)
    }
    if idx >= len(rs) {
        return 0
    }
    return rs[idx]
}

这是否值得取决于具体情况:如果使用一小组 string 调用 RuneAt(),这可能会大大提高性能。如果传递的字符串或多或少是唯一的,这将导致性能下降和大量内存使用。此实现对于并发使用也不安全。