Использование регулярных выражений для поиска и замены произвольного числа элементов на совпадение - PullRequest
4 голосов
/ 13 февраля 2020

Моя цель - распознать жирный текст в скобках на языке разметки, например:

1002 *

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

[B] blah blah [C](foo)[/C] blah [/B]

Вот моя попытка сделать это с использованием Python:

outtext = re.sub(r'(\[B\].*?)(\(.*?\))(.*?\[/B\])', r'\1[C]\2[/C]\3', intext)

Проблема в том, что она не работает, если в блоке несколько строк в скобках:

Input: [B] (foo) (bar) [/B]
Expected: [B] [C](foo)[/C] [C](bar)[/C] [/B]
Actual: [B] [C](foo)[/C] (bar) [/B]

Я знаю причина, почему это происходит, но я не знаю, как это исправить. Можно ли изменить мое регулярное выражение, чтобы оно могло находить и заменять произвольное количество строк в скобках в каждом блоке, а не только одну?

Ответы [ 3 ]

2 голосов
/ 13 февраля 2020

Проблема такого рода обычно решается заменой совпадений только внутри других совпадений. Вам нужно запустить 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> в шаблоне замены - это обратная обратная ссылка на весь матч.

2 голосов
/ 13 февраля 2020

Сначала я подумал, что одно только регулярное выражение не способно решить проблему. JvdV доказал это неправильно, молодец. Честно говоря, я больше не понимаю это регулярное выражение.

Я решил его с помощью более простого регулярного выражения и немного python

import re

intext = '[B] (foo) (bar) [/B] (not) [B] (this again) [/B]'

boldParts = re.findall(r'\[B\].*?\[/B\]', intext)
outtext = intext
for part in boldParts:
    replacement = re.sub(r'(\(.*?\))', r'[C]\1[/C]', part)
    outtext = outtext.replace(part, replacement)

print(outtext)

Сначала я ищу только жирные части в тексте тогда это легко заменить вещь в скобках. И замените его снова в тексте.

По общему признанию, это не самый короткий и не самый элегантный способ сделать это, но, возможно, немного более читаемый.

1 голос
/ 14 февраля 2020

Хорошо ... это заняло у меня некоторое время ... Я не уверен в специфике синтаксиса разметки, но сделаю некоторые предположения: текст внутри скобок может быть любым символом, кроме скобок, если только они не экранированы. Эскейп-символ - это бэсла sh. С учетом сказанного ... вот что я придумал.

>>> expr = r"""
...    \(                            # Match left paren.
...       (
...         (?:      [^ \( \) \\] |  # Match any char not a paren or escape, OR
...              \\  [  \( \) \\] |  # Match an escaped paren or escape, OR
...              \s                  # whitespace.
...          )*
...        )
...    \)                            # Match right paren. """
...
>>> re.sub(expr, r"[C](\1)[/C]",  "[B] (foo) (bar) [/B]", flags=re.VERBOSE)
'[B] [C](foo)[/C] [C](bar)[/C] [/B]'

Это также будет работать с целевыми строками, у которых в скобках не было скобок. Сокращенная форма вышесказанного - это ...

re.sub(r"\(((?:[^\(\)\\]|\\[\(\)\\]|\s)*)\)", 
       r"[C](\1)[/C]",  "[B] (foo) (bar) [/B]")

С пропущенными пробелами и комментариями ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...