Swift中如何用utf16Offset:in:处理UTF-8字节偏移索引?
解决Swift中UTF-8字节偏移的字符串范围问题(替代弃用的
encodedOffset) Apple弃用了String.Index(encodedOffset:),给出的警告如下:
'init(encodedOffset:)' is deprecated:
encodedOffset has been deprecated as most common usage is incorrect.
Use String.Index(utf16Offset:in:) to achieve the same behavior.
但encodedOffset和utf16Offset并非同一概念——我从外部服务获取的字符串索引是UTF-8字节偏移格式,直接替换会引发错误。
以字符串"👨👩👧👦"为例:它的UTF-8长度为25字节,UTF-16长度仅为11个代码单元。服务器返回的范围是{0, 25}。
原代码可正常截取目标字符串:
let index = String.Index(encodedOffset: 25) string[string.startIndex..<index] // "👨👩👧👦"
但按照Apple建议替换为String.Index(utf16Offset:in:)后会直接崩溃:
let index = String.Index(utf16Offset: 25, in: string) string[string.startIndex..<index] // Fatal error: String index range is out of bounds
此处对应的正确utf16Offset应为11而非25,但无法提前计算UTF-8字节偏移对应的UTF-16偏移,尝试String.UTF8View.Index(utf16Offset: 25, in: string)也无效。
查询Swift演进提案后得知存在UTF-8相关方法,但未找到可用接口,最终通过扩展NSMutableAttributedString实现了兼容方案:
extension NSMutableAttributedString { func attributedSubstring(utf8Range: NSRange) -> NSAttributedString { let range = rangeIn(utf8Range: utf8Range) return attributedSubstring(from: range ?? .zero) } func rangeIn(utf8Range range: NSRange) -> NSRange? { let utf8 = string.utf8 guard let lowerBound = utf8.index(utf8.startIndex, offsetBy: range.location, limitedBy: utf8.endIndex)?.samePosition(in: string.utf16) else { return nil } guard let upperBound = utf8.index(lowerBound, offsetBy: range.length, limitedBy: utf8.endIndex)?.samePosition(in: string.utf16) else { return nil } return NSRange(lowerBound ..< upperBound, in: string) } }
使用示例
转换UTF-8范围后截取:
let nsmas = NSMutableAttributedString(string: "👨👩👧👦!👩👩👦👦") if let range = nsmas.rangeIn(utf8Range: NSMakeRange(26, 25)) { nsmas.attributedSubstring(from: range) // "👩👩👦👦" }
或直接调用封装好的方法:
nsmas.attributedSubstring(utf8Range: NSMakeRange(26, 25)) // "👩👩👦👦"
内容的提问来源于stack exchange,提问作者Peter R
相关产品推荐
相关产品推荐

