【问题标题】:Swift Regex matching fails when source contains unicode characters当源包含 unicode 字符时,Swift 正则表达式匹配失败
【发布时间】:2015-06-27 16:31:51
【问题描述】:

我正在尝试使用 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 \"$1\" Second: \"$2\"")

上面的代码按预期返回“D”和“9”

如果我现在将第一行更改为包含英国“英镑”货币符号,如下所示:

let string = "£ 9"

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

我知道£ 符号将占用两个字节,但通配符前导匹配应该忽略那些不应该的吗?

谁能解释一下这里发生了什么?

【问题讨论】:

  • 我不熟悉 Swift 及其正则表达式引擎,但总的来说,当涉及 Unicode 时,我会非常惊讶地发现 \s\S 不等于 .。你为什么不在第一个分组中使用.*?也就是说,我也不完全相信这就是问题所在。我认为[0-9] 无法匹配 unicode 数字的可能性比\S 无法匹配任意非空格 unicode 字符的可能性更大。
  • Swift 确实 支持\d 字符类,那你为什么要使用[0-9]?如果你尝试匹配(.*) (\d*)(.*),你会得到匹配吗?
  • 谢谢凯尔。我使用 \s\S 是因为误读了一篇关于滥用 '.' 的文章。特点。我已将其更改为 "(.*) (\d*)(.*)" 但仍然无法匹配。我开始怀疑这是 Swift 实现中的一个错误——任何其他字符都可以匹配——例如"D$+@ 9" 但是当我在要匹配的字符串中的任何位置放置一个 '£' 符号时,它会失败!

标签: regex swift xcode6 nsregularexpression


【解决方案1】:

这可能会令人困惑。 stringByReplacingMatchesInString()的第一个参数是从NSString映射到的 Objective-C 到 String 在 Swift 中,但 range: 参数仍然是 一个NSRange。因此,您必须以单位指定范围 NSString 使用(即 UTF-16 码位的数量):

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

您也可以使用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 \"$1\" Second: \"$2\"")
println(result)
// First "£" Second: "9"

【讨论】:

  • 谢谢 Martin - 这确实解释了为什么包含货币符号的字符串的长度被报告为 4 而不是 3。它现在正确报告长度,但表达式仍然不匹配我害怕。
  • 我很抱歉马丁 - 一定是早上太早了 - 你的解决方案确实有效!非常感谢!! :-)
  • 非常感谢!我花了很多时间试图解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多