2016-08-24 103 views
4

假设以下字符串:正则表达式可选重复组

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托架内的所有选项相匹配?

+2

你可以用拉'preg_replace_callback'然后''爆炸[]'之间的所有值|'。 – chris85

+0

使用PCRE样式引擎,您只会获得捕获组的固定匹配。如果您量化更大组中的捕获组,则捕获组如果可以再次匹配则会被覆盖。这在Dot-Net中不是问题。所以你可以按照chris85提到的方式来完成,或者你可以使用'\ G'构造来挑选括号内的单个值(每个匹配1个)。 – sln

+0

@ chris85当然,可以这样做。但我想从正则表达式输出中获得所有的值。 –

回答

3

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

  1. 构建基于您输入的字符串管 |的出现次数正则表达式。

这个你可以建立的([^][|]+)最有可能重复的模式单一的正则表达式,将做一场小组赛的方式,你的愿望:

$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. 受益于其他语言功能。

上述解决方法并未提供可靠的解决方案。它正在做很多不需要的工作。下面的代码做适合的工作:

// 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