Разбор сбалансированных вложенных шаблонов вики и извлечение содержимого параметра одной строки с помощью регулярного выражения - PullRequest
1 голос
/ 01 марта 2012

Я знаю, что анализ вложенных строк или HTML лучше выполнять с помощью реального синтаксического анализатора, но в моем случае у меня есть простые шаблоны и я хотел извлечь содержимое заголовка параметра Wiki 'title' из шаблона.Мне потребовалось некоторое время, чтобы достичь этого, но благодаря инструменту регулярных выражений Ларса Олава Торвика (http://regex.larsolavtorvik.com/) и этому форуму пользователей здесь я дошел до этого. Может быть, кто-то найдет это полезным. (Мы все хотим внести свой вклад, он, не так ли? ;-) Следующий код с комментариями делает свое дело. Я должен был сделать это с осмотром утверждений, чтобы не смешивать два шаблона, если в одном из них нет заголовка.

Я пока не уверен в двух вопросах в комментариях к регулярному выражению - см. (?# Questions: …) - если я понял рекурсивную часть на (?R). Это то, что он получает свое содержимое для проверки с самого определенного определенного уровня, т.е.вторая строка регулярного выражения \{\{ и последняя строка регулярного выражения \}\}? Будет ли это правильно? И в чем разница между ++ и + до того, как альтернатива (?R) стенд будет работать одинаково, так что, похоже, при тестировании.

  1. Оригинальные вики-шаблоны на странице (самые простые):

    $wikiTemplate = "
    {{Templ1
    | title = (1. template) title
    }}
    
    {{Templ2
    | any parameter = something {{template}}
    }}
    
    {{Templ1
    | title = (3. template) title
    }}
    ";
    
  2. Замена:

    $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);
    
  3. Выходнойс заголовками, помеченными 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 Ответ

0 голосов
/ 07 ноября 2012

++ является собственническим квантификатором. Если вы добавите квантификатор повторения (+, *, {...}) с помощью +, он станет притяжательным. Это означает, что движок регулярных выражений не будет возвращаться назад и пробовать меньше повторений, как только он впервые выйдет из повторения. Таким образом, они в основном делают повторение атомной группой. Иногда это оптимизация, а иногда это действительно имеет значение. Вы можете сделать очень хорошее чтение здесь .

А по поводу вашего второго вопроса да (?R) просто попытается снова сопоставить полный паттерн. Для этого есть хорошая статья , которую можно найти в документации PHP PCRE.

Если у вас есть другие вопросы, лучше задать их по адресу Проверка кода .

...