Подтвердить что ты не робот

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

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

4b9b3361

Ответ 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.