我正在解析地址并需要在分开的匹配中获取地址和国家/地区,但这些国家/地区可能有别名,例如:
UK == United Kingdom,
US == USA == United States,
Korea == South Korea,
Run Code Online (Sandbox Code Playgroud)
等等...
所以,我所做的是创建一个大的正则表达式,其中所有可能的国家名称(至少是更可能出现的国家名称)由OR运算符分隔,如下所示:
germany|us|france|chile
Run Code Online (Sandbox Code Playgroud)
但问题在于多字国家名称及其较短版本,例如:
Republic of Moldova 和 Moldova
以此为例,我们有字符串:
'Somewhere in Moldova, bla bla, 12313, Republic of Moldova'
Run Code Online (Sandbox Code Playgroud)
我想从中得到什么:
'Somewhere in Moldova, bla bla, more bla, 12313'
'Republic of Moldova'
Run Code Online (Sandbox Code Playgroud)
但这就是我得到的:
'Somewhere in Moldova, bla bla, 12313, Republic of'
'Moldova'
Run Code Online (Sandbox Code Playgroud)
由于有几种情况,这是我到目前为止使用的:
^(.*),? \(?(republic of moldova|moldova)\)?(.*[\d\-]+.*|,.*[:/].*)?$
Run Code Online (Sandbox Code Playgroud)
因为我们可能有国家名称后的传真,电话,邮政编码或其他东西 - 我不关心 - 我使用最后一个匹配组删除它们:
(.*[\d\-]+.*|,.*[:/].*)?
Run Code Online (Sandbox Code Playgroud)
此外,有时国家名称括在括号中,所以我\(?和\)?第二个匹配组一起,并且所有国家都在其中:
(republic of moldova|moldova|...)
Run Code Online (Sandbox Code Playgroud)
问题是,当有一个条目是较大的条目的子集时,较短的条目选择较长的条目,其余的条目保留在base_address字符串中.有没有办法告诉正则表达式选择两个值马赫时最大可能的匹配?
正如m.buettner所建议的,将第一个匹配组更改(.*)为(.*?)确实修复了当前问题,但它也创建了另一个.考虑其他例子:
'新加坡国立大学化学系,新加坡4512436'
火柴:
'Department of Chemistry, National University of'
'Singapore'
Run Code Online (Sandbox Code Playgroud)
现在它很快就匹配了.
你的问题是贪婪.
.*开头的权利尽可能地匹配.这就是字符串结尾之前的一切.但是你的其他模式失败了.因此引擎回溯,丢弃与之匹配的最后一个字符.*并再次尝试其余模式(仍然失败).引擎将重复此过程(失败匹配,回溯/丢弃一个字符,再次尝试),直到它最终与模式的其余部分匹配.第一次出现这种情况时,.*所有内容都匹配Moldova(因此.*仍然消耗Republic of).然后交替(仍然无法匹配republic of moldova)将很乐意匹配moldova并返回结果.
最简单的解决方案是不重复地重复:
^(.*?)...
Run Code Online (Sandbox Code Playgroud)
请注意,量词之后的问号并不意味着"可选",而是使其"不合理".这简单地反转了行为:引擎首先尝试.*完全省略,并且在回溯过程中,在每次尝试匹配模式的其余部分失败后,它还包括一个字符.
编辑:
通常有更好的替代方法.正如您在评论中所述,不合理的解决方案带来了另一个问题,即字符串的早期部分中的国家/地区可能会匹配.你可以做什么,而不是,就是用lookarounds是确保前或后国没有字字符(字母,数字,下划线).这意味着,只有匹配的国家/地区字词,如果它被逗号或字符串的任何一端包围:
^(.*),?(?<!\w)[ ][(]?(c|o|u|n|t|r|i|e|s)[)]?(?![ ]*\w)(.*[\d\-]+.*|,.*[:/].*)?$
Run Code Online (Sandbox Code Playgroud)
由于外观实际上不是比赛的一部分,因此它们不会干扰您的模式的其余部分 - 它们只是检查比赛中特定位置的条件.我添加的两个外观确保:
请注意,我在字符类中包含空格,以及文字括号(而不是转义它们).两者都没有必要,但我更喜欢这些可读性,因此它们只是一个建议.
编辑2:
正如评论中提到的那样,如何不使用仅使用正则表达式的解决方案?
您可以拆分字符串,,然后修剪每个结果,并根据您的国家/地区列表(可能使用正则表达式)进行检查.如果您的地址中的任何组成部分与您所在的国家/地区相同,则可以退回该地址.如果存在倍数,那么至少可以检测出歧义并正确处理它.