使用 preg_match_all() 获取重复匹配

Posted

技术标签:

【中文标题】使用 preg_match_all() 获取重复匹配【英文标题】:Get repeated matches with preg_match_all() 【发布时间】:2011-09-28 14:58:59 【问题描述】:

我正在尝试让所有子字符串与乘数匹配:

$list = '1,2,3,4';
preg_match_all('|\d+(,\d+)*|', $list, $matches);
print_r($matches);

此示例如预期返回 [1] 中的最后一个匹配项:

Array
(
    [0] => Array
        (
            [0] => 1,2,3,4
        )

    [1] => Array
        (
            [0] => ,4
        )

)

但是,我想获取与(,\d+) 匹配的所有字符串,以获得类似:

Array
(
    [0] => ,2
    [1] => ,3
    [2] => ,4
)

有没有办法用preg_match_all()这样的单个函数来做到这一点?

【问题讨论】:

语言不同,但答案与***.com/questions/6571106 相同:不能,但您可以轻松地通过, 拆分。 @Kobi:谢谢你的链接。从他们所说的来看,某些语言有解决方案,php 有什么希望,还是一个明确的答案? [0] => ,2 在 PHP 中是不可能的。 ,2 是字符串还是数字? 没有。据我所知,如果您坚持使用全正则表达式解决方案,PHP 不支持捕获同一组。 谢谢科比。如果你有答案,我会接受:-) 【参考方案1】:

确实,PHP(或者更好的说法是 PCRE)不会存储重复捕获组的值以供以后访问(参见 PCRE docs):

如果重复匹配捕获子模式,则返回匹配的字符串的最后一部分。

但在大多数情况下,已知令牌 \G 可以完成这项工作。 \G 1) 匹配输入字符串的开头(如\A^,当m 未设置修饰符时)或2)从前一个匹配结束的位置开始匹配。话虽如此,你必须像下面这样使用它:

preg_match_all('/^\d+|\G(?!^)(,?\d+)\K/', $list, $matches);

见live demo here

或者如果捕获组无关紧要:

preg_match_all('/\G,?\d+/', $list, $matches);

$matches 将持有这个(见live demo):

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => ,2
            [2] => ,3
            [3] => ,4
        )

)

注意:使用\G 优于其他答案(如explode() 或后视解决方案或仅preg_match_all('/,?\d+/', ...))的好处是您能够验证输入字符串是在导出匹配项的同时以所需的格式^\d+(,\d+)*$

preg_match_all('/(?:^(?=\d+(?:,\d+)*$)|\G(?!^),)\d+/', $list, $matches);

【讨论】:

【参考方案2】:

使用lookbehind 是一种完成工作的方法:

$list = '1,2,3,4';
preg_match_all('|(?<=\d),\d+|', $list, $matches);
print_r($matches);

所有,\d+都在第0组中。

输出:

Array
(
    [0] => Array
        (
            [0] => ,2
            [1] => ,3
            [2] => ,4
        )
)

【讨论】:

@downvoter:不错的投票!这个答案有什么问题?【参考方案3】:

来自http://www.php.net/manual/en/regexp.reference.repetition.php:

当重复捕获子模式时,捕获的值是匹配最终迭代的子字符串。

还有类似的话题:

How to get all captures of subgroup matches with preg_match_all()?

【讨论】:

这些“提示”可能是问题下的评论。【参考方案4】:

只有当模式中没有使用要拆分的字符来匹配自身时,拆分才是一种选择。 我遇到过一种情况,必须将格式错误的逗号分隔行解析为许多已知选项中的任何一个。

即选项“1,2”、“2”、“2,3” 主题“1,2,3”。

在 ',' 上拆分会产生 '1'、'2' 和 '3';只有一个 ('2') 是有效匹配,这是因为分隔符也是选项的一部分。

朴素的正则表达式类似于 '~^(1,2|2|2,3)(?:,(1,2|2|2,3))*$~i',但这会遇到同组捕获问题。

我的“解决方案”是扩展正则表达式以匹配可能的最大匹配数: '~^(1,2|2|2,3)(?:,(1,2|2|2,3))?(?:,(1,2|2|2,3))?$~一世' (如果有更多选项可用,只需重复 '(?:,(1,2|2|2,3))?' 位。 这确实会导致“未使用”匹配的空字符串结果。

这不是最干净的解决方案,但在您必须处理格式错误的输入数据时有效。

【讨论】:

【参考方案5】:

根据Kobi(见上面的cmets):

PHP 不支持同一组的捕获

所以这个问题没有答案。

【讨论】:

【参考方案6】:

为什么不只是:

$ar = explode(',', $list);
print_r($ar);

【讨论】:

上面的例子是一个简化,正则表达式实际上比这更复杂。我知道如何以冗长的方式做到这一点,我只是想知道是否有更短的解决方案路径。

以上是关于使用 preg_match_all() 获取重复匹配的主要内容,如果未能解决你的问题,请参考以下文章

Preg_match_all多个内容

从字符串中获取电子邮件 - 正则表达式语法 + preg_match_all [关闭]

php 正则获取字符串中的汉字preg_match_all

Preg_match_all 在数组中返回数组?

正则表达式在 php 中捕获 <img> src [重复]

使用 preg_match_all 将所有数字与正则表达式匹配