Задача довольно понятная. На входе у нас есть шаблон регулярного выражения, который предположительно содержит именованные подшаблоны, а на выходе нам нужно получить массив имен подшаблонов:
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).*)
,
Нет необходимости считать дубликаты имен, чтобы сохранить порядок внешнего вида, или получить числовые, не захватывающие или другие типы подшаблонов. Просто список имен, если есть.
Вы можете получить список всех допустимых имен именованных групп захвата, используя
"~(?<!\\\\)(?:\\\\{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]
,
Решение Виктора кажется довольно основательным, но вот что я придумал.
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
массив, который указывает на первый набор совпадений скобок.