7

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

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

这是一个更简单的例子:

>>> 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 文档:“添加 '?' 在限定符使其以非贪婪或最小方式执行匹配之后;将匹配尽可能少的字符。"

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

4

6 回答 6

4

匹配按“从左到右”“排序”;然而,“最长”是在允许非贪婪之前使用的术语,而是表示“每个原子的首选重复次数”之类的东西。最左边比重复次数更重要。因此,"a+?$" 不会匹配 "baaaaa" 中的最后一个 A,因为在第一个 A 处的匹配在字符串中较早开始。

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

于 2011-05-04T00:37:03.947 回答
4

non-greedy 修饰符只影响匹配停止的位置,而不影响匹配开始的位置。如果您想尽可能晚地开始比赛,则必须添加.+?到模式的开头。

没有$,您的模式可以减少贪婪并更快停止,因为它不必匹配到字符串的末尾。

编辑:

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

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

正则表达式引擎将忽略直到第一个“a”之前的所有内容,因为这就是re.search工作原理。它将匹配第一个a,并且会“想要”返回一个匹配,除非它还没有匹配模式,因为它必须达到匹配$. 所以它只是一次又一次地吃那个a并检查$。如果它是贪婪的,它不会检查$之后的 each a,而只会在它无法匹配任何更多a的之后。

但在这种情况下:

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

正则表达式引擎将在吃完第一场比赛后检查它是否有完整的比赛(因为它是非贪婪的)并成功,因为$在这种情况下没有。

于 2011-05-04T00:50:18.330 回答
4

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

两者a+a+?都必须消耗a他们找到的第一个。如果a后面跟着更多a的,a+继续并消耗它们,而只满足于a+?一个。如果正则表达式还有更多内容,a+那么愿意满足于更少a的 's,并且a+?会消耗更多,如果这是实现匹配所需要的。

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

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

至于其他地方提到的最左最长规则,它从未适用于像 Python 那样的 Perl 派生的正则表达式风格。即使没有不情愿的量词,由于有序的交替,它们总是可以返回一个小于最大的匹配。我认为 Jan 的想法是正确的:Perl 派生(或正则表达式导向)风味应该称为eager,而不是 greedy 。

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

于 2011-05-05T22:39:37.083 回答
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's直到行尾。请注意,如果没有明确的分组,您将很难理解?任何事情。一般来说,无论如何,最好明确说明你用括号分组的内容。让我举一个带有显式组的例子。

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

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

于 2011-05-03T23:46:34.423 回答
1

回答原始问题:

为什么第一个 search() 跨越多个“/”而不是最短匹配?

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

回答修改后的问题:

非贪婪子模式将采用与整个模式一致的最短匹配。

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

于 2011-05-04T00:29:42.073 回答
0

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

>>> import os
>>> p = "/we/shant/see/this/butshouldseethis"
>>> os.path.basename(p)
butshouldseethis
于 2011-05-03T23:49:04.240 回答