рекурсивное регулярное выражение для обработки вложенных строк, заключенных в {| и |}

В проекте у меня есть текст с такими шаблонами:

{| текст {| text |} text |}
больше текста

Я хочу получить первую часть с помощью скобок. Для этого я использую preg_match рекурсивно. Следующий код работает отлично:

preg_match('/\{((?>[^\{\}]+)|(?R))*\}/x',$text,$matches); 

Но если я добавлю символ «|», я получил пустой результат, и я не знаю, почему:

 preg_match('/\{\|((?>[^\{\}]+)|(?R))*\|\}/x',$text,$matches); 

Я не могу использовать первое решение, потому что в тексте может существовать нечто вроде {text}. Может ли кто-нибудь сказать мне, что я здесь делаю неправильно? Спасибо

Попробуй это:

 '/(?s)\{\|(?:(?:(?!\{\||\|\}).)++|(?R))*\|\}/' 

В исходном регулярном выражении вы используете класс символов [^{}] для соответствия любому, кроме разделителя. Это нормально, когда разделители являются только одним символом, но у вас два символа. Чтобы не соответствовать многосимвольной последовательности, вам нужно что-то следующее:

 (?:(?!\{\||\|\}).)++ 

Точка соответствует любому символу (включая символы новой строки, благодаря (?s) ), но только после того, как lookahead определил, что он не является частью {| или |} . Я также сбросил вашу атомную группу ( (?>...) ) и заменил ее притяжательным квантификатором ( ++ ), чтобы уменьшить беспорядок. Но вы должны определенно использовать ту или иную часть этого регулярного выражения, чтобы предотвратить катастрофический откат .

У вас есть несколько предложений по работе с регулярными выражениями, но если вам интересно, почему ваше исходное регулярное выражение не удалось, прочитайте дальше. Проблема заключается в том, что пришло время соответствовать закрывающему тегу «|». Подэкранное выражение (?>[^{}]+) (Или [^{}]++ ) будет соответствовать значению «|», в результате чего выражается недопустимое выражение |} . Без возврата в субэкземпляре нет способа восстановить результат неудачного совпадения.

См. PHP – помощь с моей рекурсивной функцией на основе REGEX

Чтобы адаптировать его к использованию

 preg_match_all('/\{\|(?:^(\{\||\|\})|(?R))*\|\}/', $text, $matches);