Swift 当源包含 unicode 字符时正则表达式匹配失败

Swift Regex matching fails when source contains unicode characters

我正在尝试使用 NSRegularExpression 进行简单的正则表达式匹配,但是当源包含多字节字符时,我在匹配字符串时遇到了一些问题:

let string = "D 9"

// The following matches (any characters)(SPACE)(numbers)(any characters)
let pattern = "([\s\S]*) ([0-9]*)(.*)"

let slen : Int = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)

var error: NSError? = nil

var regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.DotMatchesLineSeparators, error: &error)

var result = regex?.stringByReplacingMatchesInString(string, options: nil, range: NSRange(location:0,
length:slen), withTemplate: "First \"\" Second: \"\"")

上面的代码 returns "D" 和预期的“9”

如果我现在更改第一行以包含英国 'Pound' 货币符号,如下所示:

let string = "£ 9"

然后匹配不起作用,即使表达式的 ([\s\S]*) 部分仍应匹配 any 前导字符。

我知道 £ 符号需要两个字节,但是通配符前导匹配应该忽略那些不应该吗?

谁能解释一下这是怎么回事?

这可能会造成混淆。 stringByReplacingMatchesInString()的第一个参数映射自NSString中的 Objective-C 到 Swift 中的 String,但 range: 参数仍然是 一个 NSRange。因此,您必须以单位指定范围 NSString 使用(UTF-16 代码点数):

var result = regex?.stringByReplacingMatchesInString(string,
        options: nil,
        range: NSRange(location:0, length:(string as NSString).length),
        withTemplate: "First \"\" Second: \"\"")

或者您可以使用 count(string.utf16) 而不是 (string as NSString).length .

完整示例:

let string = "£ 9"

let pattern = "([\s\S]*) ([0-9]*)(.*)"
var error: NSError? = nil
let regex = NSRegularExpression(pattern: pattern,
        options: NSRegularExpressionOptions.DotMatchesLineSeparators,
        error: &error)!

let result = regex.stringByReplacingMatchesInString(string,
    options: nil,
    range: NSRange(location:0, length:(string as NSString).length),
    withTemplate: "First \"\" Second: \"\"")
println(result)
// First "£" Second: "9"

我已经 运行 讨论过几次,Martin 的回答帮助我理解了这个问题。这是对我有用的解决方案的快速版本。

如果您的正则表达式函数包含这样构建的范围参数:

NSRange(location: 0, length: yourString.count)

你可以改成这样:

NSRange(location: 0, length: yourString.utf16.count)