preg_match_all中重复子模式仅返回最后匹配结果的问题
问题解答:正则重复捕获组只返回最后一个匹配项的原因与解决方案
这真不是preg_match_all的bug哦,其实是正则表达式里重复捕获组的固有特性导致的——当你写(\s+\d+)+这种嵌套重复的捕获组时,正则引擎每完成一次内部组(\s+\d+)的匹配,就会覆盖掉这个组之前捕获到的内容,直到整个重复结构匹配完成,所以最后只会留下最后一次匹配的结果。
为什么会这样?
正则引擎处理捕获组的逻辑是:每次捕获到内容,就更新对应组的存储值。对于(\s+\d+)+来说,它会依次匹配 9、 334、 23423343、 3433434,但每匹配一次,组1的内容就被替换成当前的匹配结果,到最后自然只剩下最后一个 3433434了。这是所有正则引擎的通用行为,不是PHP的问题,所以完全不用给PHP开发者报bug~
解决方案
这里有两种靠谱的方法可以拿到所有数字:
方法1:先验证格式,再提取数字
先确认字符串符合mem: 数字 数字 ...的格式,再单独提取所有数字,逻辑清晰又简单:
<?php $str = "mem: 9 334 23423343 3433434"; // 第一步:验证整体格式是否正确 if (!preg_match("/^mem:(\s+\d+)+$/", $str)) { throw new Exception("no match"); } // 第二步:提取所有数字 preg_match_all("/\d+/", $str, $matches); echo count($matches[0]) . " matches\n"; var_dump($matches[0]); ?>
输出会是:
4 matches array(4) { [0] => string(1) "9" [1] => string(3) "334" [2] => string(8) "23423343" [3] => string(7) "3433434" }
方法2:使用\G锚点一次性完成验证+捕获
如果你想在一次preg_match_all里搞定,可以用\G锚点——它会匹配上一次匹配结束的位置,这样既能确保整个字符串符合格式,又能捕获所有数字:
<?php $str = "mem: 9 334 23423343 3433434"; $num_matches = preg_match_all("/(?:^mem:|\G)\s+(\d+)/", $str, $matches); if (!$num_matches) { throw new Exception("no match"); } echo "$num_matches matches\n"; var_dump($matches[1]); ?>
输出和方法1一致,还能避免匹配到字符串其他无关位置的数字。
内容的提问来源于stack exchange,提问作者S. Imp
相关产品推荐
相关产品推荐

