Заменяя все регулярные выражения в одной строке

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

Пример

re.sub("(this).*(string)","this is my string",'<markup>\anygroup</markup>')
>> "<markup>this</markup> is my <markup>string</markup>"

- это возможно даже в одной строке?

Ответы

Ответ 1

Для постоянного регулярного выражения, подобного вашему примеру, выполните

re.sub("(this)(.*)(string)",
       r'<markup>\1</markup>\2<markup>\3</markup>',
       text)

Обратите внимание, что вам также нужно заключить. * в круглых скобках, если вы не хотите его потерять.

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

pattern = "(this)(.*)(string)"
re.sub(pattern,
       lambda m: ''.join('<markup>%s</markup>' % s if n % 2 == 0
                         else s for n, s in enumerate(m.groups())),
       text)

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

pattern = "()(this)(.*)(string)"
re.sub(pattern,
       lambda m: ''.join('<markup>%s</markup>' % s if n % 2 == 1
                         else s for n, s in enumerate(m.groups())),
       text)

Вы получаете идею.

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

pattern = "(this).*(string)"
def replacement(m):
    s = m.group()
    n_groups = len(m.groups())
    # assume groups do not overlap and are listed left-to-right
    for i in range(n_groups, 0, -1):
        lo, hi = m.span(i)
        s = s[:lo] + '<markup>' + s[lo:hi] + '</markup>' + s[hi:]
    return s
re.sub(pattern, replacement, text)

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

Ответ 2

re.sub() заменит все, что может. Если вы передадите ему функцию для repl, вы можете сделать еще больше.

Ответ 3

Да, это можно сделать в одной строке.

>>> re.sub(r"\b(this|string)\b", r"<markup>\1</markup>", "this is my string")
'<markup>this</markup> is my <markup>string</markup>'

\b гарантирует соответствие только полных слов.

Итак, если у вас есть список слов, которые нужно разметить, вы можете сделать следующее:

>>> mywords = ["this", "string", "words"]
>>> myre = r"\b(" + "|".join(mywords) + r")\b"
>>> re.sub(myre, r"<markup>\1</markup>", "this is my string with many words!")
'<markup>this</markup> is my <markup>string</markup> with many <markup>words</markup>!'