10

动机

我正在解析地址,需要在单独的匹配项中获取地址和国家/地区,但这些国家/地区可能有别名,例如:

UK == United Kingdom, 
US == USA == United States,
Korea == South Korea, 

等等...

解释

所以,我所做的是创建一个大的正则表达式,其中所有可能的国家名称(至少更可能出现的国家名称)由 OR 运算符分隔,如下所示:

germany|us|france|chile

但问题在于多词国家名称及其较短的版本,例如:

Republic of MoldovaMoldova

以此为例,我们有以下字符串:

'Somewhere in Moldova, bla bla, 12313, Republic of Moldova'

我想从中得到什么:

'Somewhere in Moldova, bla bla, more bla, 12313'
'Republic of Moldova'

但这就是我得到的:

'Somewhere in Moldova, bla bla, 12313, Republic of'
'Moldova'

正则表达式

由于有几种情况,这是我目前使用的:

^(.*),? \(?(republic of moldova|moldova)\)?(.*[\d\-]+.*|,.*[:/].*)?$

因为我们可能在国家名称后面有传真、电话、邮政编码或其他东西——我不在乎——我使用最后一个匹配组来删除它们:

(.*[\d\-]+.*|,.*[:/].*)?

另外,有时国家名称用括号括起来,所以我有\(?第二\)?个匹配组,所有国家都放在里面:

(republic of moldova|moldova|...)

问题

问题是,当有一个条目是较大条目的子集时,选择较短的条目而不是较长的条目,其余部分保留在 base_address 字符串中。当两个值匹配时,有没有办法告诉正则表达式选择最大可能的匹配?

编辑

  1. 我正在使用带有内置 re 模块的 Python
  2. 正如 m.buettner 所建议的,将第一个匹配组从更改(.*)(.*?)确实可以解决当前问题,但它也会创建另一个问题。考虑其他示例:

    '新加坡国立大学化学系,新加坡 4512436'

火柴:

'Department of Chemistry, National University of'
'Singapore'

现在它匹配得太早了。

4

2 回答 2

6

你的问题是贪婪

开头的.*权利尽可能地匹配。这就是字符串结束之前的所有内容。但随后你的模式的其余部分就失败了。因此引擎回溯,并丢弃匹配的最后一个字符.*并再次尝试模式的其余部分(仍然失败)。引擎将重复这个过程(匹配失败,回溯/丢弃一个字符,再试一次),直到它最终可以与模式的其余部分匹配。第一次发生这种情况是在.*匹配所有内容时Moldova(因此.*仍在消耗Republic of)。然后交替(仍然无法匹配republic of moldova)将很乐意匹配moldova并将其作为结果返回。

最简单的解决方案是使重复不贪婪:

^(.*?)...

请注意,量词后面的问号并不意味着“可选”,而是使其“不贪婪”。这只是颠倒了行为:引擎首先尝试.*完全省略,并且在回溯的过程中,在每次尝试匹配模式的其余部分失败后,它会再包含一个字符。

编辑:

通常有更好的替代不贪婪的方法。正如您在评论中所说,不贪婪的解决方案带来了另一个问题,即字符串前面部分的国家/地区可能会匹配。相反,您可以做的是使用环视来确保在国家/地区之前或之后没有单词字符(字母、数字、下划线)。这意味着,如果国家/地区单词被逗号或字符串的任一端包围,则仅匹配:

^(.*),?(?<!\w)[ ][(]?(c|o|u|n|t|r|i|e|s)[)]?(?![ ]*\w)(.*[\d\-]+.*|,.*[:/].*)?$

由于环视实际上并不是匹配的一部分,因此它们不会干扰您的模式的其余部分——它们只是检查匹配中特定位置的条件。我添加的两个外观确保:

  1. 国家前面的必填空格前没有单词字符。
  2. 国家后面没有单词字符,只用空格隔开。

请注意,我在字符类中包含了空格,以及文字括号(而不是转义它们)。两者都不是必需的,但我更喜欢这些可读性,所以它们只是一个建议。

编辑2:

正如 abarnert 在评论中提到的那样,不使用纯正则表达式解决方案怎么样?

您可以在 上拆分字符串,,然后修剪每个结果,并根据您的国家/地区列表检查这些结果(可能使用正则表达式)。如果您地址的任何组成部分与您所在的国家/地区之一相同,您可以将其退回。如果有多个,至少您可以检测到歧义并正确处理它。

于 2013-05-18T00:18:19.423 回答
1

对正则表达式中的所有替代项进行排序,只需通过排序(从最长到最短)的名称数组以编程方式创建正则表达式。然后在原子组中制作整个正则表达式(PCRE引擎有它,不知道RE引擎是否也有它)。由于原子组,正则表达式引擎永远不会回溯尝试原子组中的其他替代方案,因此您已对所有替代方案进行排序,匹配将始终是最长的。

多田。

于 2017-07-25T11:57:03.807 回答