使用正则表达式和Python进行短语匹配

时间:2012-02-21 20:02:17

标签: python regex pattern-matching

我有一些我想要匹配的短语。我使用正则表达式如下:

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

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

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

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

4 个答案:

答案 0 :(得分:5)

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

编写此正则表达式的更好方法是这样的:

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

这将尝试匹配'piston',然后立即尝试将' ring'?匹配,使其成为可选项。或者,只需确保在交替开始时出现较长的选项。

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

答案 1 :(得分:4)

来自http://www.regular-expressions.info/alternation.html(Google首次结果):

  

正则表达式引擎非常渴望。一旦找到有效匹配,它将立即停止搜索。结果是在某些情况下,替代方案的顺序很重要

一个例外:

  

POSIX标准要求返回最长匹配,无论是使用NFA还是DFA算法实现正则表达式引擎。

可能的解决方案:

  • piston( ring)?
  • (piston ring|piston)(以前放得最久)

答案 2 :(得分:2)

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

这意味着它不会尝试所有替代品,它将以匹配的第一个完成。

您可以在regular-expressions.info

上找到更多详情

对你来说,有趣的是字边界\b。我认为你在寻找的是

\bpiston(?: ring)?\b

答案 3 :(得分: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

编辑:澄清一下 - 举个例子:

text2 ='piston1 | xxx | spiston2 | xxx |活塞环| xxx | piston3'

我添加'。'匹配任何东西以显示匹配的项目

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

为了使其更准确,您需要使用后视断言。 这可以保证您匹配'|活塞',但不包括结果中的管道

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

限制从贪婪到第一个匹配字符的匹配。*?&lt;停止char&gt; 添加分组parens以排除管道。比赛。*?足够智能,可以检测是否在组内并忽略了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']

希望它有所帮助。 :)