【问题标题】:PHP preg_match_all subpattern names in a patternPHP preg_match_all 模式中的子模式名称
【发布时间】:2017-12-11 12:45:24
【问题描述】:

任务很明确。在输入中我们有一个可变的正则表达式模式,它应该包含命名的子模式,在输出中我们需要得到一个子模式名称的数组:

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 个字母数字字符和下划线组成。

由于我们不控制输入模式,我们需要考虑所有可能的命名语法。根据PHP documentation,它们是:
(?P&lt;name&gt;pattern)(?&lt;name&gt;pattern)(?'name'pattern)

我们还需要考虑嵌套子模式,例如:
(?&lt;name1&gt;.*(?&lt;name2&gt;pattern).*)

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

【问题讨论】:

标签: php regex pcre named


【解决方案1】:

您可以使用

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

查看regexonline PHP demo

重点是匹配未转义的(,后跟?,然后再跟P&lt;&lt;,然后有一个以&gt;' 结尾的组名模式接着是组名模式,然后是'

$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
)

模式详情

  • (?&lt;!\\) - 没有 \ 紧邻当前位置的左侧
  • (?:\\\\)* - 0+ 双反斜杠(允许( 之前的任何转义反斜杠)
  • \( - 一个(
  • \? - 一个?
  • (?|P?&lt;([_A-Za-z]\w{0,31})&gt;|'([_A-Za-z]\w{0,31})') - 一个分支重置组:
    • P?&lt;([_A-Za-z]\w{0,31})&gt; - 可选的P&lt;_ 或 ASCII 字母、0 到 31 个字字符(数字/字母/_)(捕获到组 1)和 &gt;
    • | - 或
    • '([_A-Za-z]\w{0,31})' - '_ 或 ASCII 字母、0 到 31 个字字符(数字/字母/_)(也捕获到第 1 组),然后是 '

组名模式都被捕获到组 1 中,您只需要获取$res[1]

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 抱歉,您的回答没有考虑其他可能的语法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-27
    相关资源
    最近更新 更多