5

我设置了一个复杂的正则表达式来从一页文本中提取数据。由于某种原因,交替的顺序不是我所期望的。一个简单的例子是:

((13th|(Executive |Residential)|((\w+) ){1,3})Floor)

简而言之,我试图获取一个楼层号,一个已知的命名楼层,作为备份,我捕获了 1-3 个未知单词,然后是楼层,以防以后复习(我实际上使用组名来识别这个但不想混淆这个问题)

问题是如果字符串是

on the 13th Floor

我不13th Floor明白on the 13th Floor,这似乎表明它与第三个交替匹配。我原以为它会匹配 13 楼。我专门设置了这个(或者我是这么认为的)来优先考虑匹配的类型,只有在错过其他匹配时才将模糊的放在最后。我猜当他们说 Regex 是贪婪时他们不是在开玩笑,但我不清楚如何将其设置为“贪婪”并按照我想要的方式行事。

4

2 回答 2

5

一个自动机值 1000 字:

正则表达式可视化

玩它

您的问题是您\w+在交替中使用了贪婪的子正则表达式。因为正如@rigderunner 在他的评论中所说,NFA 匹配最长的最左边的子字符串,\w+总是匹配之前出现的任何内容Floor,无论是一系列单词,或13thExecutiveResidential其中三个。括号不会改变交替的行为方式。

因此,它匹配而您不希望它匹配的最坏情况是:

xxxx yyyy zzz tttt Floor

您的正则表达式的问题是您希望做一些实际的正则表达式不能做的事情:如果替代方案没有成功,您期望它匹配单词。因为常规语言无法跟踪状态,所以常规正则表达式无法表达这一点。

实际上,我不确定使用某种前瞻性是否可以帮助您在一个正则表达式中做到这一点,即使可以,您最终也会得到一个非常复杂、不可读甚至可能效率不高的正则表达式。

因此,您可能更喜欢使用两个正则表达式,并从第二个正则表达式中获取组,以防第一个正则表达式失败:

((13th|Executive|Residential) +Floor)

如果没有匹配

((\w+ +){1:3}Floor)

注意:为避免重复自己,请查看其他答案,其中我列出了有关正则表达式和 NFA 的有趣资源列表。这将帮助您了解正则表达式的实际工作原理。

于 2014-05-21T12:29:49.467 回答
3

首先,这是您在自由间距模式下的正则表达式:

tidied = re.compile(r"""
    (                   # $1: ...
      (                 # $2: One ... from 3 alternatives.
        13th            # Either a1of3.
      | (               # Or a2of3 $3: One ... from 2 alternatives.
          Executive[ ]  # Either a1of2.
        | Residential   # Or a2of2.
        )               # End $3: One ... from 2 alternatives.
      | (               # Or a3of3 $4: Last match from 1 to 3 ...
          (\w+)         # $5: ...
          [ ]           #
        ){1,3}          # End $4: Last match from 1 to 3 ...
      )                 # End $2: One ... from 3 alternatives.
      Floor             #
    )                   # End $1: ...
    """, re.VERBOSE)

请注意,上述模式具有无效的额外括号。这是一个简化的表达式,它在功能上是等效的:

tidied = re.compile(r"""
    (               # $1: One ... from 4 alternatives.
      13th          # Either a1of4.
    | Executive[ ]  # Or a2of4.
    | Residential   # Or a3of4.
    | (             # Or a4of4 $2: Last match from 1 to 3 ...
        (\w+)       # $3: ...
        [ ]         #
      ){1,3}        # End $2: Last match from 1 to 3 ...
    )               # End $1: One ... from 4 alternatives.
    Floor           #
    """, re.VERBOSE)

最长的最左边匹配

在所需单词之前有四个有效的分组替代方案:Floor. 前三个选项都只有一个单词,但第四个选项匹配三个单词。NFA 正则表达式引擎从左到右工作并且总是试图找到最长的最左边的匹配。在这种情况下,当正则表达式一次遍历一个字符时,它会在每个字符位置测试所有四个选项。由于第四个选项总是可以在其他三个之前匹配两个单词,所以它总是首先匹配(假设Floor在给定文本中前面有三个单词。)。如果前面没有三个单词Floor,则前三个替代项之一可以匹配。

13th另请注意, and替代项后面不需要空格Residential,因此它只会在呈现的文本具有连接文本时匹配:ResidentialFloor13thFloor

于 2014-05-21T13:08:01.273 回答