小编典典

在不使用…范围的情况下访问字符串的随机符文元素

go

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

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

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
}

如果我想多次调用该函数怎么办?我猜我在寻找什么,就像是一个运算符/函数str[i](返回byte),该函数将rune元素放在i第-th个位置。为什么可以使用for ... range但不能通过str.At(i)例如这样的功能来访问此元素?


阅读 277

收藏
2020-07-02

共1个答案

小编典典

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

如果要有效地rune从任意索引处获取a ,则必须对字节进行解码,对此您将无能为力(for ... range此解码将完成)。没有“捷径”。所选的表示形式并不提供此功能。

如果你必须这样做频繁/多少次,你应该改变你的输入,而不是使用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]
}

这取决于情况是否值得:如果RuneAt()使用一小套strings进行调用,则可以大大提高性能。如果传递的字符串或多或少是唯一的,这将导致性能下降和大量内存使用。同样,此实现对于同时使用也不安全。

2020-07-02