代码之家  ›  专栏  ›  技术社区  ›  Mike Shiyan

PHP preg\u match\u模式中的所有子模式名称

  •  1
  • Mike Shiyan  · 技术社区  · 7 年前

    任务相当明确。在输入中,我们有一个变量regex模式,它应该包含命名的子模式,在输出中,我们需要获得一个子模式名称数组:

    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、 美国:根据 PCRE documentation 子模式名称最多由32个字母数字字符和下划线组成。

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

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

    不需要计算重复的名称,不需要保留外观顺序,也不需要获取数字、非捕获或其他类型的子模式。只要列出名字(如果有的话)。

    2 回复  |  直到 7 年前
        1
  •  3
  •   Wiktor Stribiżew    7 年前

    您可以使用获取所有有效命名捕获组名称的列表

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

    请参见 regex online PHP demo .

    关键是要匹配未替换的 ( 然后是一个 ? 然后再加上 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+双反斜杠(允许在 ( )
    • \( -a (
    • \? -a ?
    • (?|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] .

        2
  •  1
  •   Mav    7 年前

    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 指向匹配的第一组括号的数组。