Имеют ли регулярные выражения Python эквивалент атомной группировки Ruby?

Регулярные выражения в Ruby имеют функцию атомной группировки (?>regexp), описанный здесь, есть ли эквивалент в Python re модуль?

5 ответов

Решение

Python напрямую не поддерживает эту функцию, но вы можете эмулировать ее с помощью функции lookahead нулевой ширины assert ((?=RE)), который совпадает с текущей точкой с той же семантикой, что и вы, помещая именованную группу ((?P<name>RE)) внутри lookahead, а затем с помощью именованной обратной ссылки ((?P=name)) точно соответствовать любому утверждению нулевой ширины. В сочетании это дает одинаковую семантику за счет создания дополнительной группы сопоставления и большой синтаксис.

Например, указанная вами ссылка дает пример Ruby

/"(?>.*)"/.match('"Quote"') #=> nil

Мы можем подражать этому в Python так:

re.search(r'"(?=(?P<tmp>.*))(?P=tmp)"', '"Quote"') # => None

Мы можем показать, что я делаю что-то полезное, а не просто извергаю шум линии, потому что если мы изменим это так, чтобы внутренняя группа не съела финальный ", это все еще соответствует:

re.search(r'"(?=(?P<tmp>[A-Za-z]*))(?P=tmp)"', '"Quote"').groupdict()
# => {'tmp': 'Quote'}

Вы также можете использовать анонимные группы и числовые обратные ссылки, но это ужасно полно шума строк:

re.search(r'"(?=(.*))\1"', '"Quote"') # => None

(Полное раскрытие: я узнал этот трюк от Perl's perlre документация, которая упоминает это в документации для (?>...).)

В дополнение к правильной семантике, у этого также есть соответствующие свойства производительности. Если мы портировать пример из perlre:

[nelhage@anarchique:~/tmp]$ cat re.py
import re
import timeit


re_1 = re.compile(r'''\(
                           (
                             [^()]+           # x+
                           |
                             \( [^()]* \)
                           )+
                       \)
                   ''', re.X)
re_2 = re.compile(r'''\(
                           (
                             (?=(?P<tmp>[^()]+ ))(?P=tmp) # Emulate (?> x+)
                           |
                             \( [^()]* \)
                           )+
                       \)''', re.X)

print timeit.timeit("re_1.search('((()' + 'a' * 25)",
                    setup  = "from __main__ import re_1",
                    number = 10)

print timeit.timeit("re_2.search('((()' + 'a' * 25)",
                    setup  = "from __main__ import re_2",
                    number = 10)

Мы видим значительное улучшение:

[nelhage@anarchique:~/tmp]$ python re.py
96.0800571442
7.41481781006e-05

Что только становится более драматичным, когда мы увеличиваем длину строки поиска.

Согласно этой таблице ответ - нет. RFE был создан для добавления его в Python 3, но был отклонен в пользу нового regex модуль, который его поддерживает:

>>> import regex
>>> regex.match('"(?>.*)"', '"Quote"')
>>> regex.match('"(.*)"', '"Quote"')
<_regex.Match object at 0x00C6F058>

Замечания: regex также доступен для Python 2.

По состоянию на 21 марта 2022 года, примерно через 10 лет после создания этого потока, Python наконец-то добавил атомарную группировку (и притяжательное сопоставление) в модуль стандартной библиотеки в Python 3.11.0a7: commit link. Итак, теперь все ответы на эту ветку могут оказаться устаревшими; раньше только сторонниеregexмодуль имеет атомарную группировку и притяжательное сопоставление. Теперь он уже встроен в Python. reмодуль.

Казалось бы, нет.

http://www.regular-expressions.info/atomic.html

Атомная группировка поддерживается большинством современных разновидностей регулярных выражений, включая JGsoft, Java, PCRE, .NET, Perl и Ruby.

Вы можете эмулировать их отсутствие, используя группы без захвата, (?:RE), но если я правильно понял, это все равно не даст вам преимущества оптимизации.

От http://docs.python.org/2/library/re.html

(? P<имя>...)

Подобно обычным круглым скобкам, но подстрока, сопоставляемая группой, доступна в остальной части регулярного выражения через имя имени символической группы. Имена групп должны быть действительными идентификаторами Python, и каждое имя группы должно быть определено только один раз в регулярном выражении. Символическая группа также является пронумерованной группой, как если бы группа не была названа. Таким образом, группа с именем id в приведенном ниже примере также может называться пронумерованной группой 1.

Например, если шаблон (?P[a-zA-Z_]\w*), на группу можно ссылаться по ее имени в аргументах методов сопоставления объектов, таких как m.group('id') или m.end('id'), а также по имени в самом регулярном выражении (используя (?P=id)) и замещающий текст, переданный.sub () (используя \ g).

(& Dgr; P = имя)

Matches whatever text was matched by the earlier group named name.
Другие вопросы по тегам