python: использование позитивного просмотра назад и позитивного просмотра вперед

Необходимо в python в выражениях найти блоки +-/(цифры#!)+-/ (ну или от начала или от конца строки) и удалить () оставив начинку внутри

делаю так:

использую регулярное выражение

(?<=[-+*%\/]|^)(\(([0-9!#]+)\))(?=[-+*%\/]|$)

в коде

print(re.sub(r'(?<=[-+*%\/]|^)(\(([0-9!#]+)\))(?=[-+*%\/]|$)', r'\2', txt))

и получаю ошибку

raise error("look-behind requires fixed-width pattern")
re.error: look-behind requires fixed-width pattern

что не так с шаблоном и как его использовать корректно для данной задачи или как его переделать?

условно чтобы

(12!)*(7!)+9

превратилось в

12!*7!+9

Ответы (1 шт):

Автор решения: Wiktor Stribiżew

Эта проблема и её решения описаны мной на английском StackOverflow здесь.

Модуль регулярных выражений PyPi, устанавливаемый с помощью pip install regex (или pip3 install regex), решает данную проблему целиком и полностью, так как поддерживает шаблоны любой длины в блоке предварительного просмотра назад.

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

  • Перепишите шаблон так, чтобы вам не приходилось использовать чередование. Если шаблон в просмотре назад представляет собой чередование "якоря" с одним символом (как в текущем вопросе), вы можете изменить знак просмотра назад и использовать отрицательный класс символов с символом внутри. Например, (?<=\s|^)\w+(?=\s|$) обычно можно записать с помощью двойного отрицания как (?<!\S)\w+(?!\S). (?<=\s|^) находит либо пробел, либо начало строки. В библиотеке re вместо него используйте (?<!\S). (?<=^|;) можно преобразовать в (?<![^;]) (при тестировании на онлайн-сайтах добавьте \n к классу отрицательных символов, например (?<![^;\n])
  • Разделите чередования внутри блока предварительного просмотра на отдельные блоки предварительного просмотра:
    • Позитивные просмотры назад необходимо чередовать в группе (например, (?<=a|bc) следует переписать как (?:(?<=a)|(?<=bc)))
    • Отрицательные результаты просмотра можно поместить один за другим (например, (?<!^|,)"(?!,|$) должен выглядеть как (?<!^)(?<!,)"(?!,|$)).

Ответ

Вы можете использовать

(?<![^-+*%/])(\(([0-9!#]+)\))(?![^-+*%/])

См. пример работы выражения (в тесте добавлен знак переноса строки, который будет полезен при тестировании сразу нескольких строк).

→ Ссылка