Регулярные выражения Ruby имеют функцию атомной группировки (?>regexp)
, описанную здесь здесь, есть ли какой-либо эквивалент в модуле Python re
?
Правильные выражения Python имеют эквивалент атомной группировки Ruby?
Ответ 1
Python напрямую не поддерживает эту функцию, но вы можете эмулировать ее, используя утверждение lookahead с нулевой шириной ((?=RE)
), которое соответствует текущей точке с той же семантикой, которую вы хотите, поместив именованную группу ((?P<name>RE)
) внутри lookahead, а затем используя именованное backreference ((?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 perlre
, который упоминает его в документации для (?>...)
.)
В дополнение к правильной семантике это также имеет соответствующие характеристики производительности. Если мы выберем пример из perlre
:
[[email protected]:~/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)
Мы видим резкое улучшение:
[[email protected]:~/tmp]$ python re.py
96.0800571442
7.41481781006e-05
Что только становится более драматичным, поскольку мы расширяем длину строки поиска.
Ответ 2
Согласно эта таблица, ответ - нет. A RFE был создан, чтобы добавить его в Python 3, но был отклонен в пользу нового regex
, который поддерживает его:
>>> import regex
>>> regex.match('"(?>.*)"', '"Quote"')
>>> regex.match('"(.*)"', '"Quote"')
<_regex.Match object at 0x00C6F058>
Примечание: regex
также доступен для Python 2.
Ответ 3
Казалось бы, нет.
http://www.regular-expressions.info/atomic.html
Атомная группировка поддерживается большинством современных ароматов регулярных выражений, включая аромат JGsoft, Java, PCRE,.NET, Perl и Ruby.
Вы можете эмулировать их неконвертируемость, используя группы, не связанные с захватом, (?:RE)
, но если я правильно ее прочитаю, это все равно не даст вам преимуществ оптимизации.
Ответ 4
из http://docs.python.org/2/library/re.html#regular-expression-syntax
(? Р < имя > ...)
Подобно обычным скобкам, но подстрока, соответствующая группе, доступна в остальном регулярном выражении через имя имени символической группы. Имена групп должны быть действительными идентификаторами Python, и каждое имя группы должно быть определено только один раз в регулярном выражении. Символическая группа также является нумерованной группой, как если бы группа не была названа. Таким образом, группа с именем id в приведенном ниже примере также может ссылаться на пронумерованную группу 1.
Например, если шаблон имеет значение (? P [a-zA-Z_]\w *), на имя группы в аргументы можно ссылаться на методы объектов соответствия, такие как m.group('id' ) или m.end('id'), а также по имени в самом регулярном выражении (используя (? P = id)) и заменяющий текст, присвоенный .sub() (используя\g).
(? Р = имя)
Matches whatever text was matched by the earlier group named name.