Разбор сбалансированных вложенных шаблонов вики и извлечение содержимого параметра одной строки с помощью регулярного выражения
Я знаю, что анализ вложенных строк или HTML лучше выполнять с помощью реального синтаксического анализатора, но в моем случае у меня были простые шаблоны, и я хотел извлечь содержимое заголовка параметра Wiki "title" из шаблона. Мне потребовалось некоторое время, чтобы достичь этого, но благодаря инструменту регулярных выражений Ларса Олава Торвика ( http://regex.larsolavtorvik.com/) и этому форуму пользователей, здесь я дошел до этого. Может быть, кто-то найдет это полезным. (Мы все хотим внести свой вклад, он, не так ли?;-) Следующий код с комментариями делает свое дело. Я должен был сделать это с осмотром утверждений, чтобы смешать не два шаблона, если в одном из них нет заголовка.
Я еще не уверен в двух вопросах в комментариях к регулярным выражениям - см. (?# Questions: …)
- если я понял рекурсивную часть в (?R)
, Является ли это, что он получает свой контент для проверки с самого внешнего определенного уровня, то есть второй строки регулярного выражения \{\{
и последняя строка регулярного выражения \}\}
? Это было бы правильно? И в чем разница между ++
а также +
перед альтернативой (?R)
Бут работает одинаково, так кажется при тестировании.
Оригинальные вики-шаблоны на странице (самые простые):
$wikiTemplate = " {{Templ1 | title = (1. template) title }} {{Templ2 | any parameter = something {{template}} }} {{Templ1 | title = (3. template) title }} ";
Замена:
$wikiTemplate = preg_replace( array( // tag all templates with START … END and add a TITLE-placeholder before // and take care of balanced {{ … }} recursiveness "@(?s) (?# switch to dotall match, i.e. also linebreaks ) \{\{ (?# find two {{ ) (?: (?# group 1 as a non-backreferenced match ) (?: (?# group 2 as a non-backreferenced match ) (?! (?# in group 1 anything but not {{ or }} ) \{\{ | (?# or ) \}\} ) . )++ (?# Question: what is the differenc between ++ and + here? ) | (?# or ) (?R) (?# is it recursive of what is defined in the outermost, i.e. 2nd regexp line with \{\{ and last line with \}\} Question: is that here understood correctly? ) ) * (?# zero or many times of the inner regexp defintions ) \}\} (?# find two }} ) @x",// x-extended → ignore white space in the pattern // replace TITLE by single line content of title parameter "@ (?<=TITLE) (?# TITLE must preceed the following linebreak but is not backreferenced within \\0, i.e. the whole returned match) ([\n\r]+) (?#linebr in 1 may also described as . because of s-modifier dotall) (?: (?# start non-backreferenced match ) . (?# any character but not followed by START) (?!START) )+ (?# multiple times) (?: (?# start non-backreferenced match ) \|\s*title\s*=\s* (?#find the parameter '| title = ') ) ([^\r\n]+) (?#get title now to \\2 but exclude the line break. Note it is buggy when there is no line break ) (?: (?# start non-backreferenced match ) . (?# any character but not followed by END) (?!END) ) + (?# multiple times) . (?# any single character, e.g. the last because as all stuff before captures anything not followed by END) (?:END) (?#a not backreferenced END) @msx", // m-multiline, s-dotall match also linebreaks, // x-extended → ignore white space in the pattern ), array( "TITLE\nSTART\\0END", // \0 is the whole returned match, i.e. the template # replace the TITLE to TITLEtitle contentTITLE… "\\2TITLE\\0", ), $wikiTemplate ); print_r($wikiTemplate);
В результате получается заголовок, помеченный TITLE над каждым шаблоном, но только если заголовок был:
TITLE(1. template) titleTITLE START{{Templ1 | title = (1. template) title }}END TITLE START{{Templ2 | any parameter = something {{template}} }}END TITLE(3. template) titleTITLE START{{Templ1 | title = (3. template) title }}END
Есть ли у меня вопросы относительно понимания регулярных выражений или некоторых улучшений? Спасибо, Андреас.
1 ответ
++
является собственническим квантификатором. Если вы добавите квантификатор повторения (+
, *
, {...}
) с +
это становится притяжательным. Это означает, что механизм регулярных выражений не будет возвращаться назад и пробовать меньше повторений, как только он покинет повторение в первый раз. Таким образом, они в основном делают повторение атомной группой. Иногда это оптимизация, а иногда это действительно имеет значение. Вы можете сделать очень хорошее чтение здесь.
А про твой второй вопрос да (?R)
просто попытаюсь снова соответствовать полному шаблону. Для этого есть хорошая статья в документации PHP PCRE.
Если у вас есть другие вопросы, лучше задать их на Code Review.