我最近问了这个问题,答案使我加深了理解,但他们并没有解决我遇到的实际问题。因此,我将尝试如下提出类似但不同的问题。
假设我要访问的随机rune元素string。一种方法是:
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)例如这样的功能来访问此元素?
str[i]
byte
i
for ... range
str.At(i)
stringGo中的值存储文本的UTF-8编码字节序列。这是已经做出的设计决定,不会改变。
如果要有效地rune从任意索引处获取a ,则必须对字节进行解码,对此您将无能为力(for ... range此解码将完成)。没有“捷径”。所选的表示形式并不提供此功能。
如果你必须这样做频繁/多少次,你应该改变你的输入,而不是使用string,但一个[]rune,因为它是一个切片,可以有效的索引。string在Go中不是[]rune。string在Go中实际上是只读[]byte(UTF-8)。期。
[]rune
[]byte
如果您无法更改输入类型,则可以构建一个内部缓存,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进行调用,则可以大大提高性能。如果传递的字符串或多或少是唯一的,这将导致性能下降和大量内存使用。同样,此实现对于同时使用也不安全。
RuneAt()