PHP Regex, игнорировать первую группировку в чередующемся утверждении
Я пытаюсь выяснить, как захватить одно утверждение, если другое не существует, используя preg_match.
Пример текста:
<!-- InstanceBeginEditable name="doctitle" -->
<title>BU Libraries | Research Guides | Citing Your Sources</title>
<!-- InstanceEndEditable -->
<div id="standardpgt"><h1><!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --></h1></div>
Поскольку существует заголовок страницы, я хочу использовать его вместо тега doctitle. Конечно, между ними есть тонны других персонажей, но я хотел показать вам небольшой пример.
Если бы заголовок страницы не существовал, я бы хотел получить содержание документа.
Суть в том, что я не использую php-код напрямую, я передаю выражение regex через файл конфигурации, затем скрипт берет его и вытаскивает первую группу из выражения.
Вот что я придумал:
((?!.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->)<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->|<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->)
В чем проблема по какой-то причине php всегда читает первую пустую группу как группу 1, если она не работает.
Например, в приведенном выше примере текста будет возвращено
0 -> <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
1 ->
2 -> <strong>Citing Your Sources</strong>
Я не могу на всю жизнь понять, как сделать эту работу. Я также написал это регулярное выражение:
(?(?=.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->).*?<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->|.*?<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->)
Но это тоже не сработало. Огромное спасибо за помощь.
Крис
2 ответа
user178551 абсолютно правильно рекомендовал использовать конструкцию сброса ветви. В исходном регулярном выражении нет ничего плохого (кроме того факта, что его длина превышает 300 символов и он ВСЕ НА ОДНОЙ СТРОКЕ! - и что он не может поместить одну из двух альтернатив в одну группу захвата). Такое нетривиальное (мягко говоря) регулярное выражение, как это, должно быть написано в свободном интервале с отступом, чтобы вы могли фактически прочитать его. Вот ваше оригинальное регулярное выражение с добавлением некоторых разумных пробелов:
$re_OP1 = '%
( # $1:
(?!
.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->
.*?<!--\s*?InstanceEndEditable\s*?-->
)
<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?
<title>(.*?)<\/title>\s*? # $2:
<!--\s*?InstanceEndEditable\s*?-->
| <!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->
(.*?) # $3;
<!--\s*?InstanceEndEditable\s*?-->
)
%six';
Глядя на это регулярное выражение сейчас, вы можете увидеть, где вы жестко закодировали один пробел на строке с оператором ИЛИ (т.е. |<!-- InstanceBegin...
). Это приведет к тому, что регулярное выражение не совпадет с 'x'
модификатор применяется. Таким образом, заменив это пространство с \s*
и запустив его на ваших тестовых данных, вот результат, который я получаю (php-5.2.14):
Array
(
[0] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
[1] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
[2] =>
[3] => <strong>Citing Your Sources</strong>
)
Эти результаты аналогичны тем, которые вы опубликовали (но по какой-то причине ваши результаты показывают только 2 группы захвата???). Все, что нам нужно сделать сейчас, - это применить предложение по сбросу ветки user178551, и решение для регулярных выражений становится:
$re_jmr = '%
(?| # Branch reset construct. (restart counting for each alternative)
(?!
.*?<!--\s*InstanceBeginEditable\s*name="pagetitle"\s*-->
.*?<!--\s*InstanceEndEditable\s*-->
)
<!--\s*InstanceBeginEditable\s*name="doctitle"\s*-->\s*
<title>(.*?)<\/title>\s* # $1: Group 1A
<!--\s*InstanceEndEditable\s*-->
| <!--\s*InstanceBeginEditable\s*name="pagetitle"\s*-->
(.*?) # $1: Group 1B
<!--\s*InstanceEndEditable\s*-->
)
%six';
Я пошел вперед и изменил все ленивый \s*?
на жадный (потому что жадный это то, что вы хотите здесь). Я также изменил все \x22
чтобы просто "
- короче и понятнее ИМХО. И вот результаты работы с этим новым регулярным выражением сброса ветви:
Array
(
[0] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
[1] => <strong>Citing Your Sources</strong>
)
Что, (если я не ошибаюсь), именно то, что вы ищете. (Вы не предоставили тестовый пример для другой альтернативы, так что он еще не был протестирован.) Кроме этого, ваше оригинальное регулярное выражение было довольно близко.
Просто используйте шаблон сброса ветвей: (?|...) вокруг всего выражения, как в:
((?|(?!.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->)<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->|<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->))s
Из "man perlre":
"(? | pattern)" Это шаблон "сброса ветви", который обладает специальным свойством, что буферы захвата нумеруются с одной и той же начальной точки в каждой ветви чередования. Доступно начиная с Perl 5.10.0.
Буферы захвата нумеруются слева направо, но внутри этой конструкции нумерация перезапускается для каждой ветви.
Нумерация внутри каждой ветви будет такой же, как обычно, и любые буферы, следующие за этой конструкцией, будут пронумерованы, как если бы конструкция содержала только одну ветвь, то есть ту, в которой было наибольшее количество буферов захвата.
Эта конструкция будет полезна, когда вы хотите захватить одно из нескольких альтернативных совпадений.
Рассмотрим следующую схему. Числа внизу показывают, в каком буфере будет храниться захваченный контент.
# before ---------------branch-reset----------- after / ( a ) (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x # 1 2 2 3 2 3 4