В проекте у меня есть текст с такими шаблонами:
{| текст {| 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);