Имеют ли регулярные выражения 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.