模式

时间:2017-12-11 12:45:25

标签: php regex pcre named

任务很清楚。在输入中我们有一个变量正则表达式模式,据说它包含命名的子模式,在输出中我们需要获得一个子模式名称数组:

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');

P.S。:根据PCRE documentation子模式名称,最多包含32个字母数字字符和下划线。

由于我们无法控制输入模式,因此我们需要考虑所有可能的命名语法。根据{{​​3}}他们是:
(?P<name>pattern)(?<name>pattern)(?'name'pattern)

我们还需要考虑嵌套的子模式,例如:
(?<name1>.*(?<name2>pattern).*)

无需计算重复名称,保留外观顺序,或获取数字,非捕获或其他类型的子模式。如果存在,只需列出名称。

2 个答案:

答案 0 :(得分:3)

您可以使用

获取所有有效命名捕获组名称的列表
"~(?<!\\\\)(?:\\\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~"

请参阅regexonline PHP demo

重点是匹配未跟随(的{​​{1}}跟随?,然后跟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]

答案 1 :(得分:1)

Wiktor的解决方案似乎非常彻底,但这就是我想出来的。

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<后跟(.+?),后者转换为角括号之间的某个non-greedy版本。然后该函数返回$matches数组中的第一个偏移量,该偏移量指向匹配的第一组括号。