我最近问了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)
这样的文本访问?
答案 0 :(得分:3)
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
,这可能会大大提高性能。如果传递的字符串或多或少是唯一的,这将导致更差的性能和大量的内存使用。此实现对于并发使用也是不安全的。