Повторение группы захвата против захвата повторяющейся группы с вложенным шаблоном

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

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

 batman 12345 batman batman 234 batman batman 35655 batman batman 1311 batman 

Это легко достичь (просто один => (\s*batman (\d+) batman\s*) DEMO ).

Теперь я попробовал немного больше. capture tag (#capture) те же данные в capture tag (#capture)

 #capture batman 12345 batman batman 234 batman batman 35655 batman batman 1311 batman #capture #others batman 12345 batman batman 234 batman batman 35655 batman batman 1311 batman #others 

Я пытаюсь захватить строки только между #capture и я попытался

 (?:#capture)(\s*batman (\d+) batman\s*)*(?:#capture) 

который соответствует шаблону, но включает только последнюю итерацию в группе захвата, то есть $1=>batman $2=>1311 $1=>batman DEMO

Я также попытался захватить повторяющуюся группу, используя

 (?:#capture)((\s*batman (\d+) batman\s*)*)(?:#capture) 

Это захватывает все .. но в разных группах .. DEMO

Может ли кто-нибудь помочь мне понять и решить эту проблему?

Ожидаемые результаты: захватить только группу в #capture и все номера в группе, чтобы замена могла быть легкой.

Благодарю.

Вы можете использовать нефиксированный вид ширины в стиле .NET regex и использовать это регулярное выражение:

 (?s)(?<=#capture.*?)(?:batman (\d+) batman)(?=.*?#capture) 

введите описание изображения здесь

Однако этот пример работает для случая, который вы предоставили (например, он не будет работать, если в тексте больше блоков #capture...#capture ), и вам просто нужно будет добавить больше ограничений на основе контекста тега.

В PCRE / Perl вы можете добиться аналогичного результата с объявлением того, что мы хотим пропустить:

 (?(DEFINE) # Definitions (?<skip>\#others.*?\#others) # What we should skip ) (?&skip)(*SKIP)(*FAIL) # Skip it | (?<needle>batman\s+(\d+)\s+batman) # Match it 

И, скажем, замените batman new-$3 batman .

См. Эту демонстрацию в regex101 .

Поскольку PCRE не может хранить повторные захваты, как с .net-каркасом или новым модулем регулярных выражений Python, возможно использование функции \G и проверка после того, как вы убедитесь, что достигнут конец блока.

Якорь \G отмечает позицию в конце предыдущего совпадения и используется в глобальном исследовательском контексте (с preg_match_all или preg_replace* ). Полезно найти смежные результаты. Обратите внимание, что до первого совпадения \G по умолчанию по умолчанию начинается строка. Чтобы предотвратить \G чтобы преуспеть в начале строки, вам нужно добавить отрицательный lookahead (?!\A) .

 $pattern = '~ (?: # two possible branches \G(?!\A) # the contiguous branch | [#]capture \R # the start branch: only used for the first match ) (batman \h+ ([0-9]+) \h+ batman) \R # alias for any kind of newlines (?: ([#]) (?=capture) )? # the capture group 3 is used as a flag # to know if the end has been reached. # Note that # is not in the lookahead to # avoid the start branch to succeed ~x'; if (preg_match_all($pattern, $text, $matches) && array_pop($matches[3])) { print_r($matches[1]); }