regex — PHP preg_match_all имена подшаблонов в шаблоне

Задача довольно понятная. На входе у нас есть шаблон регулярного выражения, который предположительно содержит именованные подшаблоны, а на выходе нам нужно получить массив имен подшаблонов:

function get_subpattern_names($any_input_pattern) {
// What pattern to use here?
$pattern_to_get_names = '/.../';

preg_match_all($pattern_to_get_names, $any_input_pattern, $matches);

return $matches;
}

Так что вопрос в том, что использовать в качестве $pattern_to_get_names в функции выше?

Например:

get_subpattern_names('/(?P<name>\w+): (?P<digit>\d+)/');

должен вернуть:

array('name', 'digit');

П.С .: Согласно PCRE документация имена подшаблона состоят из 32 буквенно-цифровых символов и символов подчеркивания.

Поскольку мы не контролируем шаблон ввода, нам необходимо учитывать все возможные синтаксисы именования. В соответствии с PHP документация они есть:
(?P<name>pattern), (?<name>pattern) а также (?'name'pattern),

Нам также нужно учитывать вложенные подшаблоны, например:
(?<name1>.*(?<name2>pattern).*),

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

1

Решение

Вы можете получить список всех допустимых имен именованных групп захвата, используя

"~(?<!\\\\)(?:\\\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~"

Увидеть регулярное выражение и онлайн PHP демо.

Суть в том, чтобы соответствовать не покинутым ( это сопровождается ? затем следует либо P< или же < а затем имеет шаблон имени группы, заканчивающийся на > или же ' затем с шаблоном имени группы, а затем ',

$rx = "~(?<!\\\\)(?:\\\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~";
$s = "(?P<name>\w+): (?<name2>\w+): (?'digit'\d+)";
preg_match_all($rx, $s, $res);
print_r($res[1]);

доходность

Array
(
[0] => name
[1] => name2
[2] => digit
)

Детали шаблона

  • (?<!\\) — нет \ непосредственно слева от текущего местоположения
  • (?:\\\\)* — 0+ двойной обратной косой черты (чтобы разрешить любую экранированную обратную косую черту до ()
  • \( — а (
  • \? — а ?
  • (?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})') — группа сброса филиала:
    • P?<([_A-Za-z]\w{0,31})> — необязательный P, <, _ или буква ASCII, символы от 0 до 31 слова (цифры / буквы /_) (включены в группу 1) и >
    • | — или же
    • '([_A-Za-z]\w{0,31})'', _ или буква ASCII, символы от 0 до 31 слова (цифры / буквы /_) (также включены в группу 1), а затем '

Все шаблоны имен групп включены в Группу 1, вам просто нужно получить $res[1],

3

Другие решения

Решение Виктора кажется довольно основательным, но вот что я придумал.

print_r(get_subpattern_names('/(?P<name>\w+): (?P<digit>\d+)/'));

function get_subpattern_names($input_pattern){
preg_match_all('/\?P\<(.+?)\>/i', $input_pattern, $matches);
return $matches[1];
}

Это должно работать в большинстве случаев. Что еще более важно, это намного более читабельно и не требует пояснений.

В основном я ищу ?P< с последующим (.+?) что переводится как нежадным версия чего-то между угловыми скобками. Затем функция просто возвращает первое смещение в $matches массив, который указывает на первый набор совпадений скобок.

1

По вопросам рекламы [email protected]