【问题标题】:Phrase matching using regex and Python使用正则表达式和 Python 进行短语匹配
【发布时间】:2012-02-21 20:02:16
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 正则表达式贪婪仅在您使用 *+ 运算符时生效。 | 使用从左到右的第一个匹配项。

标签: python regex pattern-matching


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 我将我的列表按长度倒序排序以获得好的结果。为了清楚起见,我还听取了您的建议并使用了 \b 。感谢您的帮助!
【解决方案2】:

来自http://www.regular-expressions.info/alternation.html(第一个 Google 结果):

正则表达式引擎是急切的。一旦找到有效匹配,它将停止搜索。结果是在某些情况下,备选方案的顺序很重要

一个例外:

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

可能的解决方案:

  • piston( ring)?
  • (piston ring|piston)(把最长的放在前面)

【讨论】:

【解决方案3】:

这就是交替的行为。它尝试匹配第一个替代方案,即“活塞”,如果成功则完成。

这意味着它不会尝试所有替代方案,它会以第一个匹配的方式结束。

您可以在regular-expressions.info 上找到更多详细信息

您还可能感兴趣的是单词边界\b。我认为您正在寻找的是

\bpiston(?: ring)?\b

【讨论】:

    【解决方案4】:
    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']
    

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

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

    限制从贪心匹配到第一个匹配字符 .*? 添加分组括号以排除管道。比赛.*?足够聪明,可以检测是否在组内并忽略括号并使用下一个字符作为停止匹配标记。这似乎有效,但它忽略了最后一列。

    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']
    

    希望对您有所帮助。 :)

    【讨论】:

      猜你喜欢
      • 2021-08-17
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 2014-03-30
      • 1970-01-01
      相关资源
      最近更新 更多