正则表达式可选重复组

时间:2016-08-24 15:23:47

标签: php regex pcre

假设以下字符串:

some text here [baz|foo] and here [foo|bar|baz] and even here [option].

我设法只通过这个丑陋的正则表达式(Regex101.com demo)进行匹配:

/(?:
  \[
    (?:
      \|?
      ([^\|\[\]]+)
    )?
    (?:
      \|?
      ([^\|\[\]]+)
    )?
    (?:
      \|?
      ([^\|\[\]]+)
    )?
  \]
)/ugx

关键是我需要用方括号分组匹配。 所以目前我确实有我需要的结果:

[
  {
    "match": 1,
    "children": [
      {
        "group": 1,
        "start": 16,
        "end": 19,
        "value": "baz"
      },
      {
        "group": 2,
        "start": 20,
        "end": 23,
        "value": "foo"
      }
    ]
  },
  {
    "match": 2,
    "children": [
      {
        "group": 1,
        "start": 35,
        "end": 38,
        "value": "foo"
      },
      {
        "group": 2,
        "start": 39,
        "end": 42,
        "value": "bar"
      },
      {
        "group": 3,
        "start": 43,
        "end": 46,
        "value": "baz"
      }
    ]
  },
  {
    "match": 3,
    "children": [
      {
        "group": 1,
        "start": 63,
        "end": 69,
        "value": "option"
      }
    ]
  }
]

结果是正确的,但正则表达式仅限于模式中重复块的数量。 是否有一些解决方法使其与sqare括号内的所有选项匹配?

1 个答案:

答案 0 :(得分:3)

由于引擎无法为您提供此类功能,因此您无法在模式中递归生成捕获组。说,你有两个选择:

  1. 根据管道的出现次数构建正则表达式 输入字符串中的|
  2. 通过这种方式,您可以构建一个具有([^][|]+)重复模式的单一正则表达式,可以根据需要进行组匹配:

    $pattern = (function () use ($string) {
        $array = [];
        for ($i = 0; $i <= substr_count($string, "|"); $i++) {
            $array[] = $i == 0 ? '([^][|]+)' : '([^][|]+)?';
        }
        return implode("\|?", $array);
    })();
    

    通过提供输入字符串,如:

    some text here [baz] and here [you|him|her|foo|bar|baz|foo|option|test] and even here [another].
    

    煮熟的正则表达式将是:

    ~\[([^][|]+)\|?([^][|]+)?\|?([^][|]+)?\|?([^][|]+)?\|?([^][|]+)?\|?([^][|]+)?\|?([^][|]+)?\|?([^][|]+)?\|?([^][|]+)?]~
    

    Live demo

    然后你可以简单地使用它:

    preg_match_all("~\[$pattern]~", $string, $matches, PREG_SET_ORDER);
    

    Live demo

    这是一种解决方法,可以显示您可以节省时间并避免在构建正则表达式时遇到麻烦,正则表达式不是一个简单易用的解决方案。

    1. 受益于其他语言功能。
    2. 以上解决方法并未带来可靠的解决方案。它正在做很多不需要的工作。下面的代码确实适合这项工作:

      // Capture strings between brackets
      preg_match_all('~\[([^]]+)]~', $string, $matches);
      
      $groups = [];
      
      foreach ($matches[1] as $values) {
          // Explode them on pipe
          $groups[] = explode('|', $values);
      }
      

      输出将是:

      Array
      (
          [0] => Array
              (
                  [0] => baz
              )
      
          [1] => Array
              (
                  [0] => you
                  [1] => him
                  [2] => her
                  [3] => foo
                  [4] => bar
                  [5] => baz
                  [6] => foo
                  [7] => option
                  [8] => test
              )
      
          [2] => Array
              (
                  [0] => another
              )
      
      )
      

      Live demo