Узнайте количество групп захвата в регулярных выражениях Python - PullRequest
28 голосов
/ 24 сентября 2008

Есть ли способ определить, сколько групп захвата содержится в данном регулярном выражении?

Я бы хотел сделать следующее:

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, похоже, охватывает все основы, поэтому я собираюсь пойти на это.

Ответы [ 7 ]

35 голосов
/ 26 сентября 2008
def num_groups(regex):
    return re.compile(regex).groups
8 голосов
/ 02 февраля 2015
f_x = re.search(...)
len_groups = len(f_x.groups())
2 голосов
/ 24 сентября 2008

Что-то изнутри 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')])]))]

т.е. считать элементы типа '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. Чтобы изменить это, токенов потребуется выполнить рекурсивный поиск.

2 голосов
/ 24 сентября 2008

Свойство lastindex объекта соответствия должно быть тем, что вы ищете. См. re модуль документы .

1 голос
/ 24 сентября 2008

Прежде всего, если вам нужен только первый результат re.findall, лучше просто использовать re.search, который возвращает совпадение или None.

Для номера группы вы можете посчитать количество открытых скобок '(' за исключением тех, которые экранированы символом \ '. Вы можете использовать для этого другое регулярное выражение:

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

Обратите внимание, что это не работает, если регулярное выражение содержит группы без захвата, а также, если '(' экранируется, используя его как '[(]'). Так что это не очень надежно. Но в зависимости от того, использовать это может помочь.

0 голосов
/ 24 сентября 2008

Используя ваш код в качестве основы:

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())
0 голосов
/ 24 сентября 2008

Возможно, это неправильно, но я не думаю, что есть способ найти количество групп, которые были бы возвращены, если бы регулярное выражение соответствовало. Единственный способ, которым я могу придумать, чтобы заставить эту работу работать так, как вы хотите, - это передать в качестве аргумента число совпадений, ожидаемых вашим конкретным регулярным выражением.

Для пояснения: при успешном выполнении findall требуется, чтобы было возвращено только первое совпадение, но при неудачном завершении вы хотите получить список пустых строк? Поскольку комментарий, кажется, показывает все совпадения, возвращаемые в виде списка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...