2012-02-21 86 views
0

我有一些我想匹配的短语。我用一个正则表达式如下:使用正则表达式和Python进行短语匹配

(^|)(piston|piston ring)(|$) 

上的“活塞”使用以上,regex.match("piston ring")匹配。如果我改变正则表达式,使得更长的短语“活塞环”首先出现,然后按预期工作。

我对这种行为感到惊讶,因为我假设正则表达式的贪婪本质会尝试匹配最长的字符串“免费”。

我错过了什么?有人可以解释这一点吗?谢谢!

+1

正则表达式贪婪只有在使用'*'和'+'运算符时才会生效。 '|'使用从左到右的第一个匹配项。 – resmon6 2012-02-21 20:13:38

回答

5

当使用正则表达式交替(|),每个选项试图按照从左至右,直到比赛可以找到。因此,在您的示例中,由于可以使用piston进行匹配,因此将永远不会尝试piston ring

一种更好的方式来写这个正则表达式将是这样的:

(^|)(piston(ring)?)(|$) 

这将尝试匹配'piston',然后立即尝试匹配' ring',与?使其可选。或者,只要确保您的更长期的选择发生在交替的开始。

您可能还需要考虑使用word boundary\b,而不是(^|)(|$)

+0

+1为您的替代解决方案 – stema 2012-02-21 20:20:21

+0

我按照相反的顺序排列我的列表的长度以获得良好的结果。我也接受了您的建议,并使用\ b来清晰。谢谢您的帮助! – ccgillett 2012-02-21 20:36:53

2

这就是交替的行为。它试图匹配第一个选择,即“活塞”,如果它成功完成。

这意味着它不会尝试所有的选择,它会完成与匹配的第一个。

你可以在这里找到regular-expressions.info

更多的细节你字界限\b什么也可能是有趣的。我知道你在寻找的是

\bpiston(?: ring)?\b 
4

http://www.regular-expressions.info/alternation.html(第一谷歌的结果):

正则表达式引擎是跃跃欲试。一旦发现有效匹配,它将停止搜索。其结果是,在某些情况下,替代品的顺序事项

一个例外:

的POSIX标准授权最长匹配退还,如果不考虑正则表达式引擎是使用NFA实现或DFA算法。

可能的解决方案:

  • piston(ring)?
  • (piston ring|piston)(放之前最长)
+1

这也很好理解为什么重复表达式是贪婪的。 http://www.regular-expressions.info/repeat.html – resmon6 2012-02-21 20:19:05

0
Edit2: It wasn't clear if your test data 
contained pipes or not. I saw the pipes in 
the regex and assumed you are searching 
for pipe delim. Oh well.. not sure if below 
helps. 

使用正则表达式匹配文本的烟斗分隔将需要更多的交替回暖开始和结束列。

另一种方法呢?

text='start piston|xxx|piston ring|xxx|piston cast|xxx|piston|xxx|stock piston|piston end' 
j=re.split(r'\|',text) 

k = [ x for x in j if x.find('piston') >= 0 ] 
['start piston', 'piston ring', 'piston cast', 'piston', 'stock piston', 'piston end'] 

k = [ x for x in j if x.startswith('piston') ] 
['piston ring', 'piston cast', 'piston', 'piston end'] 

k = [ x for x in j if x == 'piston' ] 
['piston'] 

j=re.split(r'\|',text) 
if 'piston ring' in j: 
    print True 
> True 

编辑:澄清 - 借此例如:

文本2 = 'piston1 | XXX | spiston2 | XXX |活塞环| XXX | piston3'

我添加 ''搭配什么可以炫耀的项目匹配

re.findall('piston.',text2) 
['piston1', 'piston2', 'piston ', 'piston3'] 

为了使它更精确,你将需要使用向后看断言。 这样可以保证你匹配“|活塞”,但不包括管的结果,从贪婪到第一个匹配的字符

re.findall('(?<=\|)piston.',text2) 
['piston ', 'piston3'] 

限制匹配。*? <停止字符> 添加分组零件以排除管道。比赛。*?足够聪明地检测是否在组内并忽略paren并使用下一个字符作为停止匹配哨兵。这似乎工作,但它忽略了最后一列。

re.findall('(?<=\|)(piston.*?)\|',text2) 
['piston ring'] 

当你添加分组现在你只需用一个转义管指定开始

re.findall('\|(piston.*?)\|',text2) 
['piston ring'] 

要搜索的最后一列,以及添加此非分组匹配(?:\ $ ||) - 表示匹配管道(需要转义)或(|)字符串的结尾($)。 非分组匹配(?:x1 | x2)未包含在结果中。它得到优化的额外奖励。

re.findall('\|(piston.*?)(?:\||$)',text2) 
['piston ring', 'piston3'] 

最后,要解决的字符串的开头,再添改变很像以前的一个最终的字符串匹配

re.findall('(?:\||^)(piston.*?)(?:\||$)',text2) 
['piston1', 'piston ring', 'piston3'] 

希望它能帮助。 :)