2017-06-13 9 views
1

我最近问了this问题和答案增加了我的理解,但他们并没有解决我的实际问题。所以,我会试着问一个类似但不同的问题如下。访问字符串的随机符文元素,不用于...范围

假设我想访问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 
} 

如果我想多次调用这样的函数会怎样?我想我正在寻找的就像是一个像str[i](返回byte)的运算符/函数,它返回rune元素的第i位置。为什么这个元素可以使用for ... range而不是像str.At(i)这样的funtcion来访问?

+1

如果你不想在每次调用中将'string'转换为'[] rune',你需要使用'[] rune' – JimB

+0

@JimB但是,我的输入是一个字符串,我尽量避免转换字符串到'[]符文' –

+0

我的观点是,你需要将一个'字符串'转换为'[]符文',以便为此编制索引。如果你不想重复转换'string',那么使用一个'[] rune'作为参数类型,然后转换一次。 – JimB

回答

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 s,这可能会提高性能很多。如果传递的字符串或多或少是唯一的,则会导致性能下降和大量内存使用。此实现对于并发使用也不安全。