当表情符号存在时,使用 NSRegularExpression 会产生不正确的范围

Ink*_*lem 4 string ios emoji swift

我正在尝试从用户提供的字符串中解析出“@mentions”。正则表达式本身似乎可以找到它们,但当表情符号存在时,它提供的范围是不正确的。

let text = " @joe "
let tagExpr = try? NSRegularExpression(pattern: "@\\S+")
tagExpr?.enumerateMatches(in: text, range: NSRange(location: 0, length: text.characters.count)) { tag, flags, pointer in
    guard let tag = tag?.range else { return }

    if let newRange = Range(tag, in: text) {
        let replaced = text.replacingCharacters(in: newRange, with: "[email]")
        print(replaced)
    }
}
Run Code Online (Sandbox Code Playgroud)

运行时 tag=(位置:7,长度:2)

并打印出来 [email]oe

预期结果是 [email]

Lil*_*ard 8

NSRegularExpression(以及任何涉及NSRange)对 UTF16 计数/索引进行操作。就此而言,NSString.countUTF16 计数也是如此。

但在您的代码中,您告诉NSRegularExpression使用 的长度text.characters.count。这是组合字符的数量,而不是 UTF16 计数。您的字符串" @joe "有 9 个组合字符,但有 12 个 UTF16 代码单元。因此,您实际上是NSRegularExpression在告诉只查看前 9 个 UTF16 代码单元,这意味着它忽略了尾随的"oe ".

修复方法是通过length: text.utf16.count.

let text = " @joe "
let tagExpr = try? NSRegularExpression(pattern: "@\\S+")
tagExpr?.enumerateMatches(in: text, range: NSRange(location: 0, length: text.utf16.count)) { tag, flags, pointer in
    guard let tag = tag?.range else { return }

    if let newRange = Range(tag, in: text) {
        let replaced = text.replacingCharacters(in: newRange, with: "[email]")
        print(replaced)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是 /sf/ask/3240524311/ 的答案,因此我作为*那个*的重复项关闭了。不知道为什么这应该是一个“坏骗子”并重新开放。 (2认同)