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

Несоответствие Python 2 и 3 're.sub'

Я пишу функцию для разделения чисел и некоторых других вещей из текста в python. Код выглядит примерно так:

EN_EXTRACT_REGEX = '([a-zA-Z]+)'
NUM_EXTRACT_REGEX = '([0-9]+)'
AGGR_REGEX = EN_EXTRACT_REGEX + '|' + NUM_EXTRACT_REGEX

entry = re.sub(AGGR_REGEX, r' \1\2', entry)

Теперь этот код отлично работает в python3, но он не работает под python2 и получает ошибку "непревзойденной группы".

Проблема в том, что мне нужно поддерживать обе версии, и я не мог заставить ее работать правильно в python2, хотя я пробовал другие способы.

Мне любопытно, какой может быть корень этой проблемы, и есть ли какое-нибудь обходное решение для этого?

4b9b3361

Ответ 1

Я думаю, что проблема может заключаться в том, что шаблон регулярного выражения соответствует одному или другому подшаблонам EN_EXTRACT_REGEX и NUM_EXTRACT_REGEX, но не тем и другим.

Когда re.sub() соответствует альфа-символам в первом паттерне, он пытается заменить вторую ссылку на группу \2, которая терпит неудачу, потому что только первая группа соответствует - нет второй группы.

Аналогично, когда сопоставляется символ цифр, нет подменю \1, и это также не выполняется.

Вы можете видеть, что это имеет место с этим тестом в Python 2:

>>> re.sub(AGGR_REGEX, r' \1', 'abcd')    # reference first pattern
 abcd
>>> re.sub(AGGR_REGEX, r' \2', 'abcd')    # reference second pattern
Traceback (most recent call last):
....
sre_constants.error: unmatched group

Разница должна лежать в разных версиях движка regex для Python 2 и Python 3. К сожалению, я не могу дать окончательную причину разницы, однако есть документированное изменение в версии 3.5 для re.sub() в отношении несогласованных групп:

Изменено в версии 3.5: Непревзойденные группы заменяются пустой строкой.

который объясняет, почему он работает в Python >= 3.5, но не в ранних версиях: несогласованные группы в основном игнорируются.


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

import re

EN_EXTRACT_REGEX = '[a-zA-Z]+'
NUM_EXTRACT_REGEX = '[0-9]+'
AGGR_REGEX = '(' + EN_EXTRACT_REGEX + '|' + NUM_EXTRACT_REGEX + ')'
# ([a-zA-Z]+|[0-9]+)

for s in '', '1234', 'abcd', 'a1b2c3', 'aa__bb__1122cdef', '_**_':
    print(re.sub(AGGR_REGEX, r' \1', s))

Выход

 1234
 abcd
 a 1 b 2 c 3
 aa__ bb__ 1122 cdef
_**_