2

我有一个解析挪威街道地址的正则表达式:

STREET_ADDRESS_PATTERN = <<-REGEX
    ^
    (?<street_name>[\w\D\. ]+)\s+
    (?<house_number>\d+)
    (?<entrance>[A-Z])?\s*,\s*
    (
        (?<postal_code>\d{4})\s+
        (?<city>[\w\D ]+)
    )?
    $
REGEX

它更早地工作,我不记得我是否改变了一些东西,在这种情况下我改变了什么。无论如何,现在我收到了这个警告:

警告:嵌套重复运算符?和 * 被替换为 '*'

而比赛又回来了nil。谁能明白我为什么会收到此警告?


注意:我目前正在使用这个(假)地址来测试表达式:“Storgata 38H, 0273 Oslo”。

4

2 回答 2

7

让我们看一下您对糟糕的正则表达式引擎所做的事情:

(?<street_name>[\w\D\. ]+)\s+

问题出在字符类内部:[\w\D\. ]+. 以下定义来自 Ruby 的Regexp 类文档

  • /\w/- 一个单词字符([a-zA-Z0-9_])
  • /\D/- 非数字字符([^0-9])

你告诉引擎选择:

  • abcdefghijklmnopqrstuvwxyz
  • ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • 0123456789
  • _
  • 每个不是的字符 0123456789
  • .和空格

换句话说,每一个可能的字符。你也可以使用:

(?<street_name>.+)

因为那会非常贪婪。这个 Rubular 示例显示您的模式允许引擎捕获抛出的所有内容,包括几乎整个字符串Storgata 38H, 0273 Oslohttp ://rubular.com/r/nMfcB0cUdu

此外,\.inside[]是一样的,[.]因为将句点作为通配符的特殊使用会在括号内自动转义。您不需要再次转义它来尝试使其文字化,因为它已经是文字化的。

我强烈建议使用 Rubular 来查看正则表达式的每个部分,并尝试与其他几个可能的地址字符串进行匹配,看看 Rubular 是否说这些模式会符合您的期望。完成后,尝试将完整的模式放在一起。照原样,我认为您的小节正在相互作用并掩盖了一些问题,这些问题稍后会再次困扰您。


我希望 [\w\D] 会选择除数字以外的所有单词字符......有什么办法吗?

啊。让我们再次深入研究文档

POSIX 括号表达式也类似于字符类。它们为上述内容提供了一种可移植的替代方案,另外还有一个好处是它们包含非 ASCII 字符。例如,/\d/ 仅匹配 ASCII 十进制数字 (0-9);而 /[[:digit:]]/ 匹配 Unicode Nd 类别中的任何字符。

/[[:alnum:]]/ - 字母和数字字符
/[[:alpha:]]/ - 字母字符
/[[:blank:]]/ - 空格或制表符
/[[:cntrl:]]/ - 控制字符
/[[:digit:]]/ - 数字
/[[:graph:]]/ - 非空白字符(不包括空格、控制字符和类似字符)
/[[:lower:]]/ - 小写字母字符
/[[:print:]]/ - 与 [:graph:] 类似,但包含空格字符
/[[:punct:]]/ - 标点符号
/[[:space:]]/ - 空白字符([:blank:]、换行符、回车符等)
/[[:upper:]]/ - 大写字母
/[[:xdigit:]]/ - 十六进制数中允许的数字(即 0-9a-fA-F)

您想使用该/[[:alpha:]]/模式。如图所示,它只会捕获一个字符,但它会在任何 POSIX 的“字母”字符集中,这是您想要的范围:

[4] (pry) main: 0> 'æ, ø and å'.scan(/[[:alpha:]]/)
[
    [0] "æ",
    [1] "直径",
    [2] “一”,
    [3] "n",
    [4] "d",
    [5] “奥”
]

这是一个小调整:

[5] (pry) main: 0> 'æ, ø and å'.scan(/[[:alpha:]]+/)
[
    [0] "æ",
    [1] "直径",
    [2和”,
    [3] “奥”
]
于 2013-06-03T14:20:11.120 回答
1

哦,现在我明白我做了什么。我替换了'字符串的分隔符,<<-REGEX这意味着现在必须转义表达式中的所有反斜杠。改回单个滴答声解决了这个问题。在sepp2k的建议之后,我进一步将正则表达式字符串编辑为文字:

STREET_ADDRESS_PATTERN = /
    ^
    (?<street_name>[\w\D\. ]+)\s+
    (?<house_number>\d+)
    (?<entrance>[A-Z])?\s*,\s*
    (
        (?<postal_code>\d{4})\s+
        (?<city>[\w\D ]+)
    )?
    $
/xi
于 2013-06-03T10:15:23.977 回答