Получение обработчиком re.sub () на Python 3.6 с совпадениями нулевой длины в Python 3.7 - PullRequest
0 голосов
/ 06 декабря 2018

обработка совпадений нулевой длины изменилась в Python 3.7.Рассмотрим следующее с Python 3.6 (и предыдущий):

>>> import re
>>> print(re.sub('a*', 'x', 'bac'))
xbxcx
>>> print(re.sub('.*', 'x', 'bac'))
x

Мы получаем следующее с Python 3.7:

>>> import re
>>> print(re.sub('a*', 'x', 'bac'))
xbxxcx
>>> print(re.sub('.*', 'x', 'bac'))
xx

Я понимаю, что это стандартное поведение PCRE.Кроме того, re.finditer (), кажется, всегда обнаруживал дополнительное совпадение:

>>> for m in re.finditer('a*', 'bac'):
...     print(m.start(0), m.end(0), m.group(0))
...
0 0
1 2 a
2 2
3 3

Тем не менее, мне интересно узнать поведение Python 3.6 (это для хобби-проекта, реализующего sed в python ).

Я могу прийти со следующим решением:

def sub36(regex, replacement, string):

    compiled = re.compile(regex)

    class Match(object):
        def __init__(self):
            self.prevmatch = None
        def __call__(self, match):
            try:
                if match.group(0) == '' and self.prevmatch and match.start(0) == self.prevmatch.end(0):
                    return ''
                else:
                    return re._expand(compiled, match, replacement)
            finally:
                self.prevmatch = match

    return compiled.sub(Match(), string)

, которое дает:

>>> print(re.sub('a*', 'x', 'bac'))
xbxxcx
>>> print(sub36('a*', 'x', 'bac'))
xbxcx
>>> print(re.sub('.*', 'x', 'bac'))
xx
>>> print(sub36('.*', 'x', 'bac'))
x

Однако, это кажется очень сложным дляэти примеры.

Как правильно реализовать поведение Python 3.6 для совпадений нулевой длины с re.sub () с Python 3.7?

Ответы [ 3 ]

0 голосов
/ 06 декабря 2018

Ваше решение может быть в regex egg :

Regex Egg Введение

Эта реализация регулярного выражения обратно совместима сстандартный модуль 're', но предлагает дополнительную функциональность.Поведение модуля re с совпадениями нулевой ширины изменилось в Python 3.7, и этот модуль будет следовать этому поведению при компиляции для Python 3.7.


Установка:

pip install regex

Использование:

С помощью regex можно указать версию (V0, V1), для которой выполняется регулярное выражениешаблон будет скомпилирован, то есть:

# Python 3.7 and later
import regex
>>> regex.sub('.*', 'x', 'test')
'xx'
>>> regex.sub('.*?', '|', 'test')
'|||||||||'

# Python 3.6 and earlier
import regex
>>> regex.sub('(?V0).*', 'x', 'test')
'x'
>>> regex.sub('(?V1).*', 'x', 'test')
'xx'
>>> regex.sub('(?V0).*?', '|', 'test')
'|t|e|s|t|'
>>> regex.sub('(?V1).*?', '|', 'test')
'|||||||||'

Примечание:

Версия может указываться флагом VERSION0 или V0, либо (?V0) вшаблон.


Источники:

Нить регулярного выражения - выпуск 2636
регулярное выражение 2018.11.22

0 голосов
/ 05 января 2019

PCRE (включая Python 3.7+), который удовлетворяет исходным примерам, будет:

^a*|a+|(?<!a)$

https://regex101.com/r/zTpV1t/3

Однако bbaacc будет заменено на xbbxccx (вместоPython 3.6 - версия a*, которая выдала xbxbxcxcx) - для некоторых это все еще может быть достаточно.

0 голосов
/ 06 декабря 2018

Согласно 3.7 «Что нового»,

Предыдущее поведение можно восстановить, изменив шаблон на r'.+'.

См. https://docs.python.org/3/whatsnew/3.7.htmlв разделе «Изменения в Python API».Похоже, что решение, таким образом, заключается в изменении такого регулярного выражения;не похоже, что есть флаг, который вы можете передать re, чтобы запросить такое поведение.

...