如何捕捉重复模式的每一个匹配?

时间:2013-05-12 04:43:44

标签: regex preg-match preg-match-all non-greedy

我有以下正则表达式:

/xxx ([a-z]+)(?:, ([a-z]+))* xxx/

我想捕获以下测试字符串中的所有颜色:

xxx red, blue, pink, purple xxx

(现在只捕获红色和紫色)​​

打开此网址并查看匹配的群组:http://www.regex101.com/r/oZ2cH4

我已阅读以下http://www.regular-expressions.info/captureall.html但该技巧无效

(或者我做错了)

我该如何解决这个问题?

提前谢谢

2 个答案:

答案 0 :(得分:3)

您可能希望根据以前的模式匹配返回匹配组:

$word = '[a-z]+';
$sep  = '[, ]+';

$words = $captures("~($word)(?:{$sep})?~");
$of    = $captures("~xxx ({$word}(?:{$sep}{$word})*) xxx~");

print_r($words($of($subject)));

输出:

Array
(
    [0] => red
    [1] => blue
    [2] => pink
    [3] => purple
)

$captures是一个函数,它返回一个预配置的preg_match_all调用,不仅可以处理作为主题的字符串,还可以处理foreach可以操作的任何字符串:

$captures = function ($pattern, $group = 1) {
    return function ($subject) use ($pattern, $group) {
        if (is_string($subject)) {
            $subject = (array)$subject;
        }
        $captures = [];
        foreach ($subject as $step) {
            preg_match_all($pattern, $step, $matches);
            $captures = array_merge($captures, $matches[$group]);
        }
        return $captures;
    };
};

默认情况下,如上例所示,它返回第一个组(1),但可以配置它。

这允许首先匹配外部模式($of),然后在每个匹配上匹配内部模式($words)。完整的例子:

$subject = '/xxx red, blue, pink, purple xxx/';

$captures = function ($pattern, $group = 1) {
    return function ($subject) use ($pattern, $group) {
        if (is_string($subject)) {
            $subject = (array)$subject;
        }
        $captures = [];
        foreach ($subject as $step) {
            preg_match_all($pattern, $step, $matches);
            $captures = array_merge($captures, $matches[$group]);
        }
        return $captures;
    };
};

$word = '[a-z]+';
$sep  = '[, ]+';
$seq  = "";

$words = $captures("~($word)(?:{$sep})?~");
$of    = $captures("~xxx ({$word}(?:{$sep}{$word})*) xxx~");

print_r($words($of($subject)));

请参阅live-demo

答案 1 :(得分:0)

教程"Repeating a Capturing Group vs. Capturing a Repeated Group" (by regular-expressions.info)描述了如何在单次捕获中捕获所有内容“红色,蓝色,粉红色,紫色”。它建议的模式是

  

/xxx ((?:[a-z]+(?:, )?)+) xxx/

但如果这是你想要完成的,你也可以使用更简单的表达式

  

/xxx ([a-z, ]*) xxx/

我怀疑你真正想要的是分别捕捉每种颜色。这可能最好通过捕获整个列表一次,然后解析捕获的内容。