Проблема такого рода обычно решается заменой совпадений только внутри других совпадений. Вам нужно запустить re.sub
с регулярным выражением, которое будет соответствовать всем помеченным B
подстрокам, и заменить несколько вхождений строк в круглых скобках только внутри этих совпадений, используя вызываемый в re.sub
в качестве аргумента замены.
Вот решение:
import re
text = "[B] blah blah (foo) blah [/B]\n[B] (foo) (bar) [/B]"
print(re.sub(r'(?s)\[B].*?\[/B]', lambda x: re.sub(r'\([^()]*\)', r'[C]\g<0>[/C]', x.group()), text))
См. Python демо .
ПРИМЕЧАНИЕ : если у вас более длинные тексты, разверните ленивый точечный шаблон и использование
r'\[B][^[]*(?:\[(?!/?B])[^[]*)*\[/B]'
См. это демонстрационное регулярное выражение .
Вывод:
[B] blah blah [C](foo)[/C] blah [/B]
[B] [C](foo)[/C] [C](bar)[/C] [/B]
Шаблон (?s)\[B].*?\[/B]
соответствует [B]
, затем 0+ символов как можно меньше до самого левого [/B]
(примечание (?s)
позволяет .
соответствовать любому символу, включая символы разрыва строки). Затем, когда совпадение найдено, оно передается вызываемому устройству, и в этом совпадении запускается регулярное выражение \([^()]*\)
. \([^()]*\)
соответствует любой подстроке между ближайшими скобками, то есть (
, затем 0+ символов, отличных от (
и )
, а затем )
. \g<0>
в шаблоне замены - это обратная обратная ссылка на весь матч.