Группировка m групп с регулярным выражением

#python #regex

#python #регулярное выражение

Вопрос:

У меня есть регулярное выражение, которое заменяет букву n на (w{1,}) — это означает, что любое слово может заменять букву n. Теперь я хочу создать группу из m экземпляров (w{1,}) — т.е. Добавить скобки вокруг m экземпляров (w{1,}) , например:

 "("   "(w{1,}), (w{1,}), (w{1,}) .... (w{1,})"   ")", where (w{1,}) occurs m times
 

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

re.sub(w{1,}){2,}, inputstring, "(" however many instances of (w{1,}) the pattern was able to match "))

Как мне выразить в регулярном выражении, что шаблон был сопоставлен m раз? (Чтобы я мог выполнить замену, заключенную в круглые скобки).

Комментарии:

1. Не могли бы вы привести пример того, что вы пытаетесь сопоставить? В абстрактном виде это немного сложно понять, и легко пойти по неверному пути. Спасибо!

2. @zk81 У меня есть строка типа ‘n n bacon n’. Я хочу найти все случаи, когда это происходит в тексте. Так что «я люблю полоски бекона» будет соответствовать шаблону. Я хочу сгруппировать бит ‘n n’ так, чтобы я получил (я люблю) бекон (полоски)

3. Откуда вы знаете конец одного фрагмента и начало другого? Например, I love bacon strips and I love raw bacon too . Где выполняется and подгонка: с первым совпадением или со вторым совпадением?

4. Я немного смущен. Вы пишете одно регулярное выражение, чтобы создать шаблон для другого регулярного выражения? С какой из них вы просите о помощи? Кроме того, почему вы используете w{1,} , а не более лаконичный w ?

Ответ №1:

Если я правильно понимаю вопрос, вы пишете одно регулярное выражение для создания другого регулярного выражения. То есть вы используете замену регулярных выражений для построения шаблона для поиска по регулярным выражениям. Ваш ввод включает в себя какое-то значение подстановочного знака (например "n" ), которое вам нужно заменить, чтобы создать шаблон поиска. В шаблоне поиска смежные значения подстановочных знаков должны быть объединены в одну группу захвата (так "n n bacon n" будет две группы захвата, одна для первых двух слов и еще одна для последнего). Я думаю, вы можете сделать это, если сначала захватите все соседние подстановочные знаки, а затем замените отдельные экземпляры в более крупной группе.

Вот код, который это делает:

 import re

def make_pattern(template, wildcard="n"):
    replacement_pattern = r"b{0}b(?:s {0}b)*".format(wildcard)
    def replacement_func(match):
        return "("   re.sub(wildcard, r"w ", match.group())   ")"
    return re.sub(replacement_pattern, replacement_func, template)
 

b Escape-последовательности в replacement_pattern необходимы для предотвращения обработки вхождений wildcard как таковых, если они являются частью некоторого большего слова (например "n" , в конце "bacon" ). Закрытие replacement_func использует дополнительную замену регулярного выражения для замены подстановочных знаков, сохраняя при этом расстояние между ними (поэтому шаблон, подобный "n n n n" , будет соответствовать иначе, чем "n n n n" ). Я полагаю, вы могли бы вместо этого выполнить обычную замену строки (с str.replace ), если бы захотели. Я просто не мог устоять перед тремя уровнями регулярных выражений в одном решении.

Вот пример запуска:

 >>> make_pattern("n n bacon n")
'(\w \s \w ) bacon (\w )'
>>> re.findall(make_pattern("n n bacon n"), "spam spam eggs bacon and spam")
[('spam eggs', 'and')]
 

Комментарии:

1. отлично работает. из любопытства, почему функция замены определена в make_pattern? Потому что это вспомогательная функция, которая не должна быть доступна для глобального пространства имен? Это обычный шаблон python? Есть ли у него имя?

2. В данном конкретном случае необходимо, replacement_func чтобы оно было определено внутри make_pattern , потому что это «замыкание». Это означает, что он обращается к переменным (в частности, к wildcard параметру) из окружающей области. Замыкания не слишком часто используются в Python, но приятно знать, как они работают, на случай, если вы найдете их полезными. Более распространенный способ написания вложенных функций — использовать lambda выражение непосредственно в том месте, где вы используете функцию (например, при вызове re.sub ). Я не делал этого здесь, потому что строка была бы очень длинной, и не было очевидного места для ее переноса.