为什么最小(非贪婪)匹配受字符串字符“$”结尾的影响?

时间:2011-05-03 23:44:13

标签: python regex non-greedy

编辑:删除原始示例,因为它引发了辅助答案。也固定了标题。

问题是为什么正则表达式中存在“$”会影响表达式的贪婪:

这是一个更简单的例子:

>>> import re
>>> str = "baaaaaaaa"
>>> m = re.search(r"a+$", str)
>>> m.group()
'aaaaaaaa'
>>> m = re.search(r"a+?$", str)
>>> m.group()
'aaaaaaaa'

“?”似乎什么都不做。注意当“$”被删除时,然后“?”受到尊重:

>>> m = re.search(r"a+?", str)
>>> m.group()
'a'

编辑: 换句话说,“a +?$”匹配所有a而不是最后一个,这不是我的预期。这是正则表达式“+?”的描述来自python docs: “加'?'在限定符之后,它以非贪婪或最小的方式执行匹配;尽可能少的字符将被匹配。“

在这个例子中似乎不是这样的:字符串“a”与正则表达式“a +?$”相匹配,那么为什么字符串“baaaaaaa”上的相同正则表达式的匹配只是单个a (最右边的一个)?

6 个答案:

答案 0 :(得分:4)

匹配由"left-most, then longest"“排序”;然而,“最长”是在允许非贪婪之前使用的术语,而是指“每个原子的首选重复次数”。最重要的是比重复次数更重要。因此,“a +?$”将与“baaaaa”中的最后一个A不匹配,因为第一个A的匹配在字符串中较早开始。

(在评论中的OP澄清之后答案发生了变化。请参阅之前文本的历史记录。)

答案 1 :(得分:4)

非贪婪修饰符仅影响匹配停止的位置,从不影响启动的位置。如果您想尽可能晚地开始比赛,则必须将.+?添加到模式的开头。

如果没有$,您的模式允许不那么贪婪并且更快停止,因为它不必匹配字符串的结尾。

编辑:

更多细节......在这种情况下:

re.search(r"a+?$", "baaaaaaaa")

正则表达式引擎会忽略所有内容,直到第一个'a',因为这就是re.search的工作原理。它会匹配第一个a,并且“想”返回匹配项,但它与模式不匹配,因为它必须达到$的匹配项。所以它只是一次吃a个,并检查$。如果它是贪婪的,则不会在每个$之后检查a,但只有在它不再匹配a之后才会检查。

但在这种情况下:

re.search(r"a+?", "baaaaaaaa")

正则表达式引擎会在吃完第一场比赛后检查它是否完全匹配(因为它不贪婪)而成功因为在这种情况下没有$

答案 2 :(得分:3)

正则表达式中$的存在不会影响表达式的贪婪。它只是增加了另一个必须满足的条件才能使整个比赛成功。

a+a+?都需要使用他们找到的第一个a。如果a之后有更多a,那么a+会继续使用它们,而a+?只会满足它。如果正则表达式还有其他内容,那么a+会愿意接受更少的a,而a+?会消耗更多,如果这就是实现匹配所需要的。< / p>

使用a+$a+?$,您添加了另一个条件:匹配至少一个a ,然后字符串的结尾。 a+最初仍会消耗所有a,然后将其移至锚点($)。第一次尝试成功,因此a+不需要回馈任何a

另一方面,a+?在转交给a之前最初只消耗一个$。这失败了,所以控制权返回到a+?,这会消耗另一个a并再次放手。所以,直到a+?消耗最后a并且$最终成功。是的,a+?$确实匹配与a相同数量的a+$,但它不情愿地这样做,而不是贪婪。

至于其他地方提到的最左边最长的规则,从未适用于像Python这样的Perl派生的正则表达式。即使没有不情愿的量词,由于ordered alternation,它们总是会返回一个不那么大的匹配。我认为Jan有正确的想法:Perl派生(或正则表达式)的风格应该被称为eager,而不是贪婪。

我认为最左边最长的规则仅适用于POSIX NFA正则规则,它们在底层使用NFA引擎,但需要返回与DFA(文本导向)正则表达式相同的结果。

答案 3 :(得分:1)

这里有两个问题。您在没有指定组的情况下使用了group(),我可以告诉您正常表达式显式括号组的行为和没有括号内组的行为混淆。您正在观察的此行为没有括号只是Python提供的快捷方式,您需要阅读group()上的文档才能完全理解它。

>>> import re
>>> string = "baaa"
>>> 
>>> # Here you're searching for one or more `a`s until the end of the line.
>>> pattern = re.search(r"a+$", string)
>>> pattern.group()
'aaa'
>>> 
>>> # This means the same thing as above, since the presence of the `$`
>>> # cancels out any meaning that the `?` might have.
>>> pattern = re.search(r"a+?$", string)
>>> pattern.group()
'aaa'
>>> 
>>> # Here you remove the `$`, so it matches the least amount of `a` it can.
>>> pattern = re.search(r"a+?", string)
>>> pattern.group()
'a'

底线是字符串a+?匹配一个a,句点。但是,a+?$会匹配a直到行尾。请注意,如果没有明确的分组,您将很难让?表达任何意义。一般来说,无论如何,最好明确说明你用括号分组的内容。让我举个例子 with 显式组。

>>> # This is close to the example pattern with `a+?$` and therefore `a+$`.
>>> # It matches `a`s until the end of the line. Again the `?` can't do anything.
>>> pattern = re.search(r"(a+?)$", string)
>>> pattern.group(1)
'aaa'
>>>
>>> # In order to get the `?` to work, you need something else in your pattern
>>> # and outside your group that can be matched that will allow the selection
>>> # of `a`s to be lazy. # In this case, the `.*` is greedy and will gobble up
>>> # everything that the lazy `a+?` doesn't want to.
>>> pattern = re.search(r"(a+?).*$", string)
>>> pattern.group(1)
'a'

编辑:删除与旧版本问题相关的文字。

答案 4 :(得分:1)

回答原始问题:

  

为什么第一次搜索()跨越   多个“/”而不是采取   最短的比赛?

非贪婪的子模式将使最短的匹配与后续的整个模式保持一致。在您的示例中,最后一个子模式是$,因此之前的子模式需要延伸到字符串的末尾。

回答修订后的问题:

非贪婪的子模式将使最短的匹配与后续的整个模式保持一致。

另一种观察方式:非贪婪的子模式最初会匹配最短的匹配。但是,如果这会导致整个模式失败,则会使用额外的字符重试。此过程将继续,直到子模式失败(导致整个模式失败)或整个模式匹配。

答案 5 :(得分:0)

除非您的问题不包含一些重要信息,否则您不需要也不应该使用正则表达式执行此任务。

>>> import os
>>> p = "/we/shant/see/this/butshouldseethis"
>>> os.path.basename(p)
butshouldseethis