Рекурсивный шаблон в регулярном выражении

Это очень похоже на Регулярное выражение для соответствия внешним скобкам, однако я специально хочу знать, как это сделать или можно ли это сделать рекурсивный шаблон regex? Я еще не нашел пример python, используя эту стратегию, поэтому подумайте, что это должен быть полезный вопрос!

Я видел несколько претензий что рекурсивные шаблоны могут использоваться для сопоставления сбалансированной круглой скобки, но нет примеров с использованием python regex (Примечание: re не поддерживает рекурсивный шаблон, вам нужно использовать регулярное выражение).

Один утверждение заключается в том, что синтаксис b(?:m|(?R))*e где:

b - это то, что начинает конструкцию, m - это то, что может происходить в середине конструкции, а e - это то, что может произойти в конце конструкции


Я хочу извлечь совпадения для внешних фигурных скобок в следующем:

"{1, {2, 3}} {4, 5}"
["1, {2, 3}", "4, 5"]  # desired

Обратите внимание, что это легко сделать для внутренних фигурных скобок:

re.findall(r"{([^{}]*)}", "{1, {2, 3}} {4, 5}")
['2, 3', '4, 5']

(В моем примере я использовал finditer (над объектами совпадения), см. здесь.)

Итак, я надеялся, что следующее или какое-то изменение будет работать:

regex.findall(r"{(:[^{}]*|?R)}", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:[^{}]*|?R)})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:.*|(?R))*})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:.*)|(?R)*})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:[^{}])|(?R)})", "{1, {2, 3}} {4, 5}")

но меня обманывает либо [], либо error: too much backtracking.

Можно ли извлекать объекты соответствия для внешней скобки с помощью рекурсии регулярных выражений?


Очевидно, я рискую быть сбитым с помощью:

Я хочу подчеркнуть, что речь идет о как использовать рекурсивный шаблон (который, если мое понимание верное, выводит нас за рамки регулярного разбора языка, так что это может быть действительно возможно!). Если это можно сделать, это должно быть более чистым решением.

Ответ 1

Образец:

{((?>[^{}]+|(?R))*)}

Вы можете увидеть, как это работает для вашего примера:

regex.findall("{((?>[^{}]+|(?R))*)}", "{1, {2, 3}} {4, 5}")
# ['1, {2, 3}', '4, 5']

Объяснение:

Часть m должна исключать скобки. Использование атомной группы необходимо, если вы хотите в то же время разрешить квантификатор для [^{}] и повторить группу без проблем с катастрофическим обратным слежением. Чтобы быть более ясным, если последний закрывающий фигурный скобок отсутствует, этот механизм регулярных выражений будет возвращать атомную группу атомной группой вместо символа по символу. Чтобы привести домой этот момент, вы можете сделать так, чтобы квантификатор был таким же: {((?>[^{}]+|(?R))*+)} (или {((?:[^{}]+|(?R))*+)}, поскольку атомная группа больше не полезна).

Атомная группа (?>....) и притяжательный квантор ?+, *+, ++ являются двумя сторонами одной и той же функции. Эта функция запрещает движку регулярных выражений возвращаться в группу символов, которая становится "атомом" (то, что вы не можете разделить на более мелкие части).

Основными примерами являются следующие два шаблона, которые всегда терпят неудачу для строки aaaaaaaaaab:

(?>a+)ab
a++ab

то есть:

regex.match("a++ab", "aaaaaaaaaab")
regex.match("(?>a+)ab", "aaaaaaaaaab")

Когда вы используете (?:a+) или a+, двигатель regex (по умолчанию) записывает (в предвидении) все позиции возврата для всех символов. Но когда вы используете атомную группу или притяжательный квантификатор, те положения обратной цитаты больше не записываются (кроме начала группы). Поэтому, когда возникает механизм обратного слежения, последний символ "a" не может быть возвращен. Можно вернуть только всю группу.

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

{([^{}]*+(?:(?R)[^{}]*)*+)}

Ответ 2

Я смог сделать это без проблем с синтаксисом b(?:m|(?R))*e:

{((?:[^{}]|(?R))*)}

Демо


Я думаю, что ключ от того, что вы пытаетесь, состоит в том, что повторение не продолжается m, а целая группа (?:m|(?R)). Это то, что позволяет рекурсию с помощью ссылки (?R).