Регулярные выражения PCRE с использованием подпрограмм именованных шаблонов - PullRequest
5 голосов
/ 09 февраля 2011

Я экспериментирую с именованными функциями регулярного выражения subpattern / 'subroutine' в PHP PCRE, и я надеюсь, что кто-то может объяснить следующий странный вывод:

$re = "/
(?(DEFINE)
    (?<a> a )
)

^(?&a)$

/x";

var_dump(preg_match($re, 'a', $match)); // (int) 1 as expected
var_dump($match); // Array( [0] => 'a' ) <-- Why?

Я не могу понять, почему именованная группа "a" не находится в результате (с содержанием "a"). Изменение preg_match на preg_match_all помещает "a" и "1" в данные о совпадении, но оба содержат только пустую строку.

Мне действительно нравится идея написания регулярных выражений таким образом, поскольку вы можете сделать их невероятно мощными, в то же время сохраняя их очень легко обслуживаемыми (см. этот ответ для хорошего примера этого), однако, если подшаблоны недоступно в данных о матче, тогда это не особо полезно.

Я что-то здесь упускаю или я должен просто оплакивать то, что могло бы быть, и двигаться дальше?

1 Ответ

5 голосов
/ 09 февраля 2011

Совершенно очевидно, что эти подшаблоны не будут захватывать группу - их основное назначение - использовать их более одного раза, так что вы не сможете действительно захватить их всех. Кроме того, если по умолчанию захватывать все подшаблоны, вы не сможете , а не захватить группу, в которой вы этого не хотите - не лучшее поведение по умолчанию. Противоположность тривиальна - вы можете сделать это, добавив еще одну группу вокруг оператора (?&a).
Я не смог найти ссылку на это на PCRE.org . Наиболее близким является то, что имеет значение, потому что вы не соответствуете (?<a>...) напрямую (хотя вы можете ожидать пустую группу):

Любые захватывающие скобки, которые устанавливаются во время вызова подпрограммы вернуться к своим предыдущим значениям впоследствии.

Это более понятно в Руководстве по Perl (соответствующая часть выделена):

Пример того, как это можно использовать, выглядит следующим образом:

/(?<NAME>(?&NAME_PAT))(?<ADDR>(?&ADDRESS_PAT))
(?(DEFINE)
(?<NAME_PAT>....)
(?<ADRESS_PAT>....)
)/x

Обратите внимание, что буферы захвата, соответствующие внутри рекурсии, недоступны после того, как рекурсия вернет , поэтому необходим дополнительный слой буферов захвата.

...