【问题标题】:Why does re.findall() find more matches than re.sub()?为什么 re.findall() 找到的匹配项比 re.sub() 多?
【发布时间】:2013-05-04 20:42:53
【问题描述】:

考虑以下几点:

>>> import re
>>> a = "first:second"
>>> re.findall("[^:]*", a)
['first', '', 'second', '']
>>> re.sub("[^:]*", r"(\g<0>)", a)
'(first):(second)'

re.sub() 的行为最初更有意义,但我也可以理解re.findall() 的行为。毕竟,您可以匹配 first: 之间仅包含非冒号字符(恰好为零)的空字符串,但为什么 re.sub() 的行为方式不同?

最后一个命令的结果不应该是(first)():(second)()吗?

【问题讨论】:

  • findall 的文档说返回空匹配,但sub 的文档没有这样说。
  • @VaughnCato re.sub('.*', 'foo', '') 虽然返回 'foo'
  • @VaughnCato 就我个人而言,我不认为这是一个令人满意的解释。为什么空字符串甚至被认为是匹配的?不像其他非比赛之间明显不同,但它也可以被计入上一场比赛。这不是,但它仍然不是正确的方法。为什么不匹配 : 之后的空字符串呢?虽然我认为这里不适合回答这些问题。
  • @StjepanBakrac:它尝试了first 并立即找到了匹配项,然后匹配项在位置 5 重新开始,它只能匹配一个空字符串。引擎将前进到位置 6 并重新开始匹配,发现 second 等。
  • @StjepanBakrac:问题是引擎不会在某个位置开始匹配,如果它已经找到一个匹配开始在相同的位置。因此,您将得到 1 个空字符串。这都是引擎的实现,如果我们不考虑引擎,争论正则表达式实际匹配的内容实际上是没有意义的。

标签: python regex


【解决方案1】:

您使用允许空匹配的 *:

'first'   -> matched
':'       -> not in the character class but, as the pattern can be empty due 
             to the *, an empty string is matched -->''
'second'  -> matched
'$'       -> can contain an empty string before,
             an empty string is matched -->''

引用documentation for re.findall()

空匹配包含在结果中,除非它们触及另一个匹配的开头。

documentation for re.sub() 中解释了您在子结果中看不到空匹配项的原因:

模式的空匹配仅在与前一个匹配不相邻时被替换。

试试这个:

re.sub('(?:Choucroute garnie)*', '#', 'ornithorynque') 

现在是这样的:

print re.sub('(?:nithorynque)*', '#', 'ornithorynque')

没有连续的#

【讨论】:

  • the sub function don't replace any empty matches that are adjacent to another match. 这似乎很好地描述了这种行为。尤其是re.sub('(?=.)', '#', 'text')re.sub('^', '#', 'text')
【解决方案2】:

出于某种原因,处理空匹配的算法有所不同。

findall 的情况下,它的工作方式(优化版本)如下:对于每个可能的起始索引 0 ['first', '', 'second', ''] 的原因是在firstsecond 之后立即找到空匹配项,而不是在冒号之后——因为从该位置开始查找匹配项会返回完整字符串second

sub 的情况下,正如您所注意到的,不同之处在于它明确忽略了在另一个匹配之后立即出现的长度为 0 的匹配。虽然我明白为什么这可能有助于避免 sub 的意外行为,但我不确定为什么会有这种差异(例如,为什么 findall 不使用相同的规则)。

【讨论】:

    【解决方案3】:
    import re
    a = "first:second:three"
    print re.findall("[^:]*", a)
    

    返回所有匹配模式的子字符串,在这里,它给出了

    >>> 
    ['first', '', 'second', '', 'three', '']
    

    sub() 用于替换,并将用您的替换替换最左边不重叠的模式出现。前

    import re
    a = "first:second:three"
    print re.sub("[^:]*", r"smile", a)
    

    给了

    >>> 
    smile:smile:smile
    

    您可以命令将出现的次数替换为第四个参数,count:

    【讨论】:

    • 这并不是问题的真正答案。了解为什么它们的行为不同会很有趣。
    • 我尝试了几个例子来展示两者是如何工作的,但我完全不明白为什么我应该得到这个反对票。也许您应该节省时间不写评论或提出自己的建设性答案
    • 那不是我。但是您的回答仍然与问题无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2021-01-24
    • 2015-04-04
    相关资源
    最近更新 更多