【问题标题】:python match only captures first and last group - am I misunderstanding something?python match 只捕获第一组和最后一组 - 我误解了什么吗?
【发布时间】:2012-06-14 03:31:36
【问题描述】:

我正在编写一个应该与一系列作者匹配的小 Python 脚本,为此我使用了re-module。我遇到了一些意想不到的事情,我已经能够将其简化为以下非常简单的示例:

>>> import re
>>> s = "$word1$, $word2$, $word3$, $word4$"
>>> word = r'\$(word\d)\$'
>>> m = re.match(word+'(?:, ' + word + r')*', s)
>>> m.groups()
('word1', 'word4')

所以我定义了一个“基本”正则表达式,它与我输入的主要部分相匹配,具有一些可识别的特征(在这种情况下,我使用了$-signs),然后我尝试匹配一个单词加上一个可能的单词额外的单词列表。

我原以为 m.groups() 会显示:

>>> m.groups()
('word1', 'word2', 'word3', 'word4')

但显然我做错了什么。我想知道为什么这个解决方案不起作用以及如何更改它,以便我得到我正在寻找的结果。顺便说一句,这是在 Linux 机器上使用 Python 2.6.6,以防万一。

【问题讨论】:

    标签: python regex match


    【解决方案1】:

    虽然您正在匹配每个$word#$,但第二个捕获组不断被最后一个匹配的项目替换。

    我们来看看调试器:

    >>> expr = r"\$(word\d)\$(?:, \$(word\d)\$)*"
    >>> c = re.compile(expr, re.DEBUG)
    literal 36
    subpattern 1
      literal 119
      literal 111
      literal 114
      literal 100
      in
        category category_digit
    literal 36
    max_repeat 0 65535
      subpattern None
        literal 44
        literal 32
        literal 36
        subpattern 2
          literal 119
          literal 111
          literal 114
          literal 100
          in
            category category_digit
        literal 36
    

    如您所见,只有 2 个捕获组:subpattern 1subpattern 2。每次找到另一个$word#$ 时,subpattern 2 就会被覆盖。

    至于潜在的解决方案,我建议使用re.findall() 而不是re.match()

    >>> s = "$word1$, $word2$, $word3$, $word4$"
    >>> authors = re.findall(r"\$(\w+)\$", s)
    >>> authors
    ['word1', 'word2', 'word3', 'word4']
    

    【讨论】:

    • 感谢您的推荐和额外的解释 - re.DEBUG 对我来说是一个新选项;很高兴知道!
    • @JakobvanBethlehem 您还应该查看re.VERBOSE,它可以让您拆分正则表达式、添加 cmets 等并仍然编译它。基本上,在每一行写下你的 RE 的一部分,用习惯的 # 添加 cmets(并确保将它放在三引号中)。可以让你的 RE 在特别复杂时更具可读性。
    【解决方案2】:

    您的正则表达式中只有两个捕获组。请改用re.findall(word, s)

    regex module 支持重复捕获。

    【讨论】:

    • 对于我正在尝试处理的成熟案例,我真的不愿意使用 findall - 感谢您建议 regex,也许这就是要走的路
    【解决方案3】:

    当您有可选或重复的捕获组时,如您所做的那样:

    (?:, \$(word\d)\$)*
    

    该正则表达式只有一个位置用于返回该组中捕获的文本,尽管它与您的字符串的 3 个部分匹配,因此它包含 last 这样的子字符串。

    要查找所有子字符串,您可以使用findall 或在其他分隔符上标记字符串。

    【讨论】:

    • 感谢您的建议 - 尽管在完全成熟的情况下我正在尝试处理我试图阻止使用 findall (它实际上匹配我不想要的小片段好吧,是正则表达式的不同其他部分的顺序使事情起作用)也许我可以以我还没有想到的方式解决它
    • 嗯,另一种解决方案是在捕获组中捕获整个可选部分,因此您有\$(word\d)\$((?:, \$word\d\$)*)。然后,您的正则表达式将返回两个组,组1 带有第一个单词,组2 带有其余匹配项,然后您可以在其上运行findallsplit。这可以确保第二个字符串至少遵循您给定的模式。
    • 感谢您考虑一下这个问题 - 这也是我正在考虑的相同方向,所以我想我会尝试一下。
    【解决方案4】:

    你可以避免这样的正则表达式:

    >>> s = "$word1$, $word2$, $word3$, $word4$"
    >>> s.replace('$','').split()
    ['word1,', 'word2,', 'word3,', 'word4']
    

    使用正则表达式,您可以改用findall()

    >>> re.findall(word, s)
    ['word1', 'word2', 'word3', 'word4']
    

    【讨论】:

    • 感谢您查看我的问题。我知道你提出的所有问题——我只是不能在我试图处理的成熟案例中使用它们..
    猜你喜欢
    • 2012-11-21
    • 2020-10-22
    • 2019-05-24
    • 2019-08-26
    • 2021-12-04
    • 2011-05-16
    • 2018-05-16
    • 2015-10-14
    • 1970-01-01
    相关资源
    最近更新 更多