找出Python正则表达式中捕获组的数量

时间:2020-03-06 14:39:39  来源:igfitidea点击:

有没有一种方法可以确定给定正则表达式中有多少个捕获组?

我希望能够做到以下几点:

def groups(regexp, s):
    """ Returns the first result of re.findall, or an empty default

    >>> groups(r'(\d)(\d)(\d)', '123')
    ('1', '2', '3')
    >>> groups(r'(\d)(\d)(\d)', 'abc')
    ('', '', '')
    """
    import re
    m = re.search(regexp, s)
    if m:
        return m.groups()
    return ('',) * num_of_groups(regexp)

这使我可以执行以下操作:

first, last, phone = groups(r'(\w+) (\w+) ([\d\-]+)', 'John Doe 555-3456')

但是,我不知道如何实现num_of_groups。 (当前,我只是解决它。)

编辑:按照rslite的建议,我将re.findall替换为re.search

sre_parse似乎是最健壮和最全面的解决方案,但是它需要遍历树并且显得有些沉重。

MizardX的正则表达式似乎涵盖了所有基础,因此我将继续讨论。

解决方案

首先,如果只需要re.findall的第一个结果,最好只使用返回匹配项或者None的re.search。

对于组号,我们可以计算开括号"("的数量,但用" "转义的括号除外。我们可以为此使用另一个正则表达式:

def num_of_groups(regexp):
    rg = re.compile(r'(?<!\)\(')
    return len(rg.findall(regexp))

请注意,如果正则表达式包含非捕获组,并且如果将'('用作'[(]'进行转义,则此方法也无效。帮助。

匹配对象的lastindex属性应该是我们要寻找的。请参阅re模块文档。

sre_parse内部的某些内容可能会有所帮助。

乍一看,也许是这样的:

>>> import sre_parse
>>> sre_parse.parse('(\d)\d(\d)')
[('subpattern', (1, [('in', [('category', 'category_digit')])])), 
('in', [('category', 'category_digit')]), 
('subpattern', (2, [('in', [('category', 'category_digit')])]))]

IE。计算" subpattern"类型的项目:

import sre_parse

def count_patterns(regex):
    """
    >>> count_patterns('foo: \d')
    0
    >>> count_patterns('foo: (\d)')
    1
    >>> count_patterns('foo: (\d(\s))')
    1
    """
    parsed = sre_parse.parse(regex)
    return len([token for token in parsed if token[0] == 'subpattern'])

请注意,此处我们仅计算根级别模式,因此上一个示例仅返回1. 要更改此设置,将需要递归搜索令牌。

可能是错误的,但是我不认为有一种方法可以找到在匹配正则表达式的情况下可以返回的组数。我能想到的以这种方式进行工作的唯一方法是传递特定正则表达式期望的匹配数作为参数。

不过要澄清一下:findall成功时,我们只希望返回第一个匹配项,但是失败时,我们需要一个空字符串列表?因为该注释似乎显示所有匹配项都以列表形式返回。

使用代码作为基础:

def groups(regexp, s):
    """ Returns the first result of re.findall, or an empty default

    >>> groups(r'(\d)(\d)(\d)', '123')
    ('1', '2', '3')
    >>> groups(r'(\d)(\d)(\d)', 'abc')
    ('', '', '')
    """
    import re
    m = re.search(regexp, s)
    if m:
        return m.groups()
    return ('',) * len(m.groups())

def num_groups(regex):
    return re.compile(regex).groups