RegEx在PHP中命名捕获组

时间:2015-07-28 13:17:02

标签: php regex

我有以下正则表达式来捕获数字列表(最终会比这更复杂):

$list = '10,9,8,7,6,5,4,3,2,1';

$regex = 
<<<REGEX
    /(?x)
    (?(DEFINE)
        (?<number> (\d+) )
        (?<list> (?&number)(,(?&number))* )
    )
    ^(?&list)/
REGEX;

$matches = array();
if (preg_match($regex,$list,$matches)==1) {
    print_r($matches);
}

哪个输出:

Array ( [0] => 10,9,8,7,6,5,4,3,2,1 ) 

如何捕获$ matches数组中列表中的个别数字?尽管在数字周围放置了一个捕获组(\ d +),我似乎无法做到这一点。

修改

为了更清楚,我想最终使用递归,因此爆炸并不理想:

$match = 
<<<REGEX
    /(?x)
    (?(DEFINE)
        (?<number> (\d+) )
        (?<member> (?&number)|(?&list) )
        (?<list> \( ((?&number)|(?&member))(,(?&member))* \) ) 
    )
    ^(?&list)/
REGEX;

3 个答案:

答案 0 :(得分:2)

(?(DEFINE)...)部分的目的只是定义您可以在以后定义部分本身或主模式中使用的命名子模式。由于这些子模式未在主模式中定义,因此它们不会捕获任何内容,而引用(?&number)只是子模式\d+的一种别名,并且不会捕获任何内容

字符串示例:1abcde2

如果我使用此模式:/^(?<num>\d).....(?&num)$/仅在 num 组中捕获1(?&num)不会捕获任何内容,它只是别名\d
/^(?<num>\d).....\d$/产生完全相同的结果。

另一点澄清。使用PCRE(PHP正则表达式引擎),捕获组(已命名或未命名)只能存储一个值,即使您重复它也是如此。

你的方法的主要问题是你试图同时做两件事:

  1. 您想检查字符串的格式。
  2. 您想要提取未知数量的项目。
  3. 只有在特定情况下才能这样做,但一般情况下不可能。

    例如,对于没有嵌套元素的平面列表,例如:$list = '10,9,8,7,6,5,4,3,2,1';,您可以使用像preg_match_all这样的函数以这种方式多次重复使用相同的模式:

    if (preg_match_all('~\G(\d+)(,|$)~', $list, $matches) && !end($matches[2])) {
        // \G ensures that results are contiguous
        // you have all the items in $matches[1] 
        // if the last item of $matches[2] is empty, this means
        // that the end of the string is reached and the string
        // format is correct
        echo '<°)))))))>';
    }
    

    现在,如果您有一个像$list = '10,9,(8,(7,6),5),4,(3,2),1';这样的嵌套列表,并且您希望例如检查格式并生成如下树结构:

    [ 10, 9, [ 8, [ 7, 6 ], 5 ], 4 , [ 3, 2 ], 1 ]
    

    一次通过你不能做到这一点。您需要一种模式来检查整个字符串格式,另一种模式来提取元素(以及使用它的递归函数)。

    <<<FORGET_THIS_IMMEDIATELY

    顺便说一句,你可以使用evalstrtr,但这是一种非常肮脏和危险的方式:

    eval('$result=[' . strtr($list, '()', '[]') . '];');
    

    FORGET_THIS_IMMEDIATELY;

答案 1 :(得分:1)

如果你想获得逗号分隔数字的数组,那么就爆炸:

$numbers = explode(',', $matches[0]); //first parameter is your delimiter what the string will be split up by. And the second parameter is the initial string
print_r($numbers);

输出:

Array(
    [0] => 10,
    [1] => 9,
    [2] => 8,

答案 2 :(得分:0)

对于这个简单的列表,这就足够了(如果你必须使用正则表达式):

$string = '10,9,8,7,6,5,4,3,2,1';
$pattern = '/([\d]+),?/';

preg_match_all($pattern, $string, $matches);

print_r($matches[1]);