正则表达式匹配OR中较长的字符串

时间:2013-05-18 00:00:26

标签: python regex

动机

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

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. 我正在使用带内置模块的Python
  2. 正如m.buettner所建议的那样,将第一个匹配组从(.*)更改为(.*?)确实修复了当前问题,但它也创建了另一个。考虑其他例子:

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

  3. 匹配

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

    现在它很快就匹配了。

2 个答案:

答案 0 :(得分:6)

您的问题是greediness

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

最简单的解决方案是不重复地重复:

^(.*?)...

请注意,量词之后的问号并不意味着“可选”,而是使其“不合适”。这简单地反转了行为:引擎首先尝试完全忽略.*,并且在回溯过程中,在每次尝试匹配模式的其余部分失败后,它还包含一个字符。

修改

通常有更好的替代方法。正如您在评论中所述,不合理的解决方案带来了另一个问题,即字符串的早期部分中的国家/地区可能会匹配。你可以做的是使用lookarounds确保国家之前或之后没有单词字符(字母,数字,下划线)。这意味着,只有匹配的国家/地区字词,如果它被逗号或字符串的任何一端包围:

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

由于外观实际上不是比赛的一部分,因此它们不会干扰您的模式的其余部分 - 它们只是检查比赛中特定位置的状况。我添加的两个外观确保:

  1. 在国家/地区之前的强制性空格之前没有字符。
  2. 除了空格之外,国家之间没有单词字符。
  3. 请注意,我在字符类中包含空格,以及字面括号(而不是转义它们)。两者都没有必要,但我更喜欢这些可读性,所以它们只是一个建议。

    编辑2:

    正如评论中提到的那样,如何不使用仅使用正则表达式的解决方案?

    您可以在,上拆分字符串,然后修剪每个结果,并针对您的国家/地区列表(可能使用正则表达式)进行检查。如果您的地址中的任何组成部分与您所在的国家/地区相同,则可以退回该地址。如果存在倍数,那么至少可以检测出歧义并正确处理它。

答案 1 :(得分:1)

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

多田。