访问字符串的随机符文元素而不使用for ...范围

时间:2017-06-13 16:42:45

标签: string go type-conversion byte rune

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

假设我想访问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元素。为什么可以使用for ... range访问此元素,但不能通过像str.At(i)这样的文本访问?

1 个答案:

答案 0 :(得分:3)

Go中的

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

如果你想在任意索引处有效地从中获取rune,你必须解码字节,你不能做任何事情(for ... range执行此解码) 。没有"快捷方式"。所选择的表示并不能提供开箱即用的功能。

如果您经常/多次这样做,您应该更改输入,而不是使用string而是[]rune,因为它是一个切片,可以有效地编入索引。 Go中的string不是[]rune。 Go中的string实际上是只读[]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()来调用string,这可能会大大提高性能。如果传递的字符串或多或少是唯一的,这将导致更差的性能和大量的内存使用。此实现对于并发使用也是不安全的。