UTF8字符串长度和Go vs Swift中的索引

she*_*lll 0 string utf-8 go swift

我在Go和Swift中有应用程序处理字符串,例如查找子字符串及其索引.起初它甚至可以使用多字节字符(例如表情符号),使用Go utf8.RuneCountInString()和Swift的原生字符串.

但是有一些UTF8字符可以打破字符串长度和子字符串的索引,例如字符串"Lorem ?? ipsum":

Go的utf8.RuneCountInString("Lorem ?? ipsum")返回17,起始索引ipsum为12.

Swift的"Lorem ?? ipsum".count返回16,起始索引ipsum为11.

使用Swift String utf8,utf16或者使用cast来NSString提供不同的长度和索引.还有其他表情符号由多个其他表情符号组成,它们提供更有趣的数字.

这是Go 1.8和Swift 4.1.

有没有办法用Go和Swift获得相同的字符串长度和子字符串索引?

编辑

我基于@ MartinR的答案创建了一个Swift String扩展:

extension String {
    func runesRangeToNSRange(from: Int, to: Int) -> NSRange {
        let length = to - from
        let start = unicodeScalars.index(unicodeScalars.startIndex, offsetBy: from)
        let end = unicodeScalars.index(start, offsetBy: length)
        let range = start..<end

        return NSRange(range, in: self)
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*n R 7

在Swift中,a Character是一个"扩展的字形集群",每个"","","✌️","",""都算作单个字符.

我没有使用Go的经验,但正如我从Go中了解字符串,符号,符文和字符一样,"符文"是一个Unicode代码点,它基本上对应UnicodeScalar于Swift中的一个.

在您的示例中,差异来自"✌️",它被视为单个Swift字符,但是由两个Unicode标量构建:

print("??".count) // 1
print("??".unicodeScalars.count) // 2
Run Code Online (Sandbox Code Playgroud)

下面是一个如何根据Unicode标量计算长度和偏移量的示例:

let s = "Lorem ?? ipsum"
print(s.unicodeScalars.count) // 17

if let idx = s.range(of: "ipsum") {
    print(s.unicodeScalars.distance(from: s.startIndex, to: idx.lowerBound)) // 12
}
Run Code Online (Sandbox Code Playgroud)

如您所见,这与Go中的示例中的数字相同.