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