为什么re.sub('。*?',' - ',' abc')返回' -a-b-c - '而不是' -------'?

时间:2015-05-11 05:12:02

标签: python regex

这是python2.7的结果。

>>> re.sub('.*?', '-', 'abc')
'-a-b-c-'

我认为的结果应如下。

>>> re.sub('.*?', '-', 'abc')
'-------'

但事实并非如此。为什么呢?

4 个答案:

答案 0 :(得分:4)

我所知道的这种行为的最佳解释来自regex PyPI包,它旨在最终替换re(虽然它已经是这样的现在很久了。)

  

有时候不清楚应该如何处理零宽度匹配。例如,在匹配> 0个字符后,*。匹配0个字符吗?

     

大多数正则表达式实现遵循Perl(PCRE)的引导,但re模块有时不遵循。 Perl行为似乎是最常见的(并且re模块有时肯定是错误的),因此在版本1中,regex模块遵循Perl行为,而在版本0中,它遵循遗留的re行为。

     

示例:

# Version 0 behaviour (like re)
>>> regex.sub('(?V0).*', 'x', 'test')
'x'
>>> regex.sub('(?V0).*?', '|', 'test')
'|t|e|s|t|'

# Version 1 behaviour (like Perl)
>>> regex.sub('(?V1).*', 'x', 'test')
'xx'
>>> regex.sub('(?V1).*?', '|', 'test')
'|||||||||'

(?VX)在正则表达式中设置版本标志。第二个例子是你所期望的,并且据说是PCRE所做的。 Python的re有点不标准,并且保留,因为它可能完全是由于向后兼容性问题。 I've found an example of something similar(与re.split)。

答案 1 :(得分:3)

您确定正确解释了re.sub的文档吗?

  

*?+???&#39; &#39;,&#39; +&#39;和&#39;?& #39;资格赛都是贪心的;它们匹配尽可能多的文本。有时这种行为是不可取的;如果   RE&lt;。&gt;与'<H1>title</H1>'匹配,它将匹配。{   整个字符串,而不仅仅是'<H1>'。添加&#39;?&#39;在资格赛之后   使它以非贪婪或极简的方式进行比赛;很少   尽可能匹配的字符。使用。*?在以前   表达式仅匹配&#39;。

添加?会将表达式转换为非贪婪的表达式。

<强>贪婪:

re.sub(".*", "-", "abc")

<强>非贪婪:

re.sub(".*?", "-", "abc")

更新:FWIW re.sub完全符合预期:

>>> from re import sub
>>> sub(".*?", "-", "abc")
'-a-b-c-'
>>> sub(".*", "-", "abc")
'-'

请参阅@ BrenBarn关于你获得-a-b-c-的原因的精彩答案:)

这是对正在发生的事情的直观表达:

.*?

Regular expression visualization

Debuggex Demo

答案 2 :(得分:3)

对于您新编辑的问题:

.*?可以匹配任意数量的字符,包括零。所以它的作用是匹配字符串中每个位置的零个字符:在“a”之前,在“a”和“b”之间,等等。它用连字符替换每个零宽度匹配,给出结果你看。

正则表达式不会尝试逐个匹配每个字符;它尝试匹配字符串中的每个位置。你的正则表达式允许它匹配零个字符。因此它在每个位置匹配零并继续下一个位置。你似乎在想,在像“abc”这样的字符串中,“b”之前有一个位置,“b”之内有一个位置,“b”之后有一个位置,但是里面没有位置“个性。如果它匹配在“b”之前开始的零个字符,则它尝试的下一个事项是匹配“b”之后的开始。在三个字符的字符串中,你无法使正则表达式匹配七次,因为只有四个位置匹配。

答案 3 :(得分:1)

详细说明Veedrac's answer,不同的实现在FindAll(或ReplaceAll)操作中对零宽度匹配有不同的处理方式。在不同的实现中可以观察到两种行为,Python re只是选择遵循第一行实现。

1。始终在零宽度匹配上碰到一个字符

在Java和JavaScript中,零宽度匹配会导致索引碰到一个字符,因为保留在同一索引中会导致FindAll或ReplaceAll操作中出现无限循环。

因此,此类实现中FindAll操作的输出最多可包含一个从特定索引开始的匹配。

默认的Python re包可能也遵循相同的实现(Ruby似乎也是如此)。

2。禁止在相同索引

的下一场比赛中进行零宽度匹配

在PHP中,它提供了一个基于PCRE库的包装器,零宽度匹配会导致索引立即碰撞。相反,它将设置一个标志(PCRE_NOTEMPTY),要求下一个匹配(从同一索引开始)为非零宽度匹配。如果匹配成功,它将沿着匹配的长度(非零)碰撞;否则,它会撞到一个角色。

顺便说一句,PCRE库不提供内置的FindAll或ReplaceAll操作。它实际上是由PHP包装器提供的。

因此,此类实现中FindAll操作的输出最多可包含2个从同一索引开始的匹配。

Python regex包可能遵循这一实现方式。

这条实现更复杂,因为它需要实现FindAll或ReplaceAll以保持是否禁止零宽度匹配的额外状态。开发人员还需要在使用低级匹配API时跟踪这些额外标志。