【问题标题】:Capturing repeating subpatterns in Python regex在 Python 正则表达式中捕获重复的子模式
【发布时间】:2022-01-25 05:10:38
【问题描述】:

在匹配电子邮件地址时,在匹配yasar@webmail 之类的内容后,我想捕获一个或多个(\.\w+)(我所做的有点复杂,这只是一个示例),我试过了添加 (.\w+)+ ,但它只捕获最后一场比赛。例如,yasar@webmail.something.edu.tr 匹配但仅在yasar@webmail 部分之后包含.tr,因此我丢失了.something.edu 组。我可以在 Python 正则表达式中执行此操作,还是建议您先匹配所有内容,然后再拆分子模式?

【问题讨论】:

  • Python Issue 7132 中提出了捕获重复表达式,但被拒绝。但是第三方regex 模块支持它。
  • @ToddOwen 但是,这在 2.7 中不是可能吗?我不知道什么时候成为可能。但是,stackoverflow.com/a/9765037/3541976 的答案似乎对我来说在 2.7 中使用 re 模块工作得很好。
  • @MichaelOhlrogge 第 7132 期是关于如果捕获括号在 inside 重复时会发生什么。该问题尚未解决,并且仍将仅保留最后一场比赛。如您链接到的答案中所述,一种可能的解决方法是将捕获括号围绕放置一个重复模式。 (请注意,(?: ...) 没有捕获括号)。
  • @ToddOwen 明白了,谢谢,这是一个有用的说明!

标签: python regex


【解决方案1】:

re 模块不支持重复捕获(regex 支持):

>>> m = regex.match(r'([.\w]+)@((\w+)(\.\w+)+)', 'yasar@webmail.something.edu.tr')
>>> m.groups()
('yasar', 'webmail.something.edu.tr', 'webmail', '.tr')
>>> m.captures(4)
['.something', '.edu', '.tr']

在您的情况下,我稍后会拆分重复的子模式。它产生了一个简单易读的代码,例如,参见@Li-aung Yip's answer中的代码。

【讨论】:

  • 出于好奇,当匹配重复捕获时如何编写替换模式? \1\2\3等的含义会根据你匹配(\.\w+)的次数而改变吗?
  • @Li-aung Yip:\1对应m.group(1);意义没有改变。您可以使用函数作为替换模式并在其中调用m.captures()
  • 在您的示例中,\1\2\3 的含义很明显,因为它们只捕获一次。但是\4对应(\.\w+)+是什么意思呢? \4 似乎是“与第 4 个捕获组匹配的最后一个子字符串”,在本例中为 .tr
  • @Li-aung Yip: m.groups() 上面明确显示了\4 是什么。
  • 含义没有改变:\4m.group(4) 不管它是什么。
【解决方案2】:

这将起作用:

>>> regexp = r"[\w\.]+@(\w+)(\.\w+)?(\.\w+)?(\.\w+)?(\.\w+)?(\.\w+)?"
>>> email_address = "william.adama@galactica.caprica.fleet.mil"
>>> m = re.match(regexp, email_address)
>>> m.groups()
('galactica', '.caprica', '.fleet', '.mil', None, None)

但仅限于最多六个子组。更好的方法是:

>>> m = re.match(r"[\w\.]+@(.+)", email_address)
>>> m.groups()
('galactica.caprica.fleet.mil',)
>>> m.group(1).split('.')
['galactica', 'caprica', 'fleet', 'mil']

请注意,只要电子邮件地址简单,正则表达式就可以了 - 但是这会破坏各种事情。有关电子邮件地址正则表达式的详细处理,请参阅this question

【讨论】:

    【解决方案3】:

    您可以通过这样做来解决(\.\w+)+ 仅捕获最后一个匹配项的问题:((?:\.\w+)+)

    【讨论】:

    • 对于缩写(如果您使用小写):re.sub(ur'((?:[a-z]\.){2,})', lambda m: m.group(1).replace('.', ''), text)
    • 谢谢。我能够添加括号允许我匹配一个重复的子模式,但是在匹配中存在一个与模式的最后一个匹配的组。我没有看到(?: ...) 是一个非捕获组。 docs.python.org/2/library/re.html#regular-expression-syntax 添加解决了这个问题。
    【解决方案4】:

    这就是你要找的东西:

    >>> import re
    
    >>> s="yasar@webmail.something.edu.tr"
    >>> r=re.compile("\.\w+")
    >>> m=r.findall(s)
    
    >>> m
    ['.something', '.edu', '.tr']
    

    【讨论】:

    • 这与yasar@webmail 不匹配。因此,如果电子邮件地址以外的其他内容以多个句点分隔,则它很容易获得误报结果。
    • OP 已经清楚地写道,这只是一个例子,他想要做的事情更复杂。因此,我的回答。
    • 是的,但问题是您的解决方案即使在 OP 给出的问题的简化版本上也不起作用。对于任何对 RegEx 有最基本了解的人来说,您的解决方案都非常简单。所有其他答案都比较复杂,因为这确实是一个非常重要的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    相关资源
    最近更新 更多