PCRE2正则优化:将引号/无引号src值捕获至同一分组
正则优化:统一捕获img标签的src属性值(PCRE2/PHP >=7.3)
环境与需求
正则环境:PCRE2(PHP >= 7.3)
现有一段包含多个<img>标签的多行字符串,需实现以下正则捕获:
- 捕获所有包含
src属性的完整<img>标签; - 捕获上述
src属性的属性值。
src属性格式规则:
- 可使用双引号、单引号包裹,或无引号包裹;
- 带引号时,属性值内不得包含对应引号;
- 无引号时,属性值内不得包含空白字符或
>。
当前问题
现有正则已实现基本匹配,但带引号的src值被捕获至$matches[2],无引号的被捕获至$matches[3],需要手动复制数据到同一分组,现需优化正则,让两类src值直接捕获到同一个分组中。
原实现代码
$code = <<<EOD 1. <img width src = one height> 2. <notAtStart><img src=two height> 3. NotAtAll 4. <img width src="three"><notAtEnd> 5. <notAtStart><img src = 'four' /><notAtEnd> 6. <img src =five><test> 7. <img WithoutSrc> EOD; $regex='/(<IMG(?=\s).*\sSRC\s*=\s* (?(?=["\']) .(.+?) ["\'] | (.+?) [\s>] ) (?(?<!>).*?>) )/ix'; preg_match_all($regex, $code, $matches); echo PHP_EOL . "Matches:"; // print all groups: print_r($matches); // copy matches captures in $matches[2] to $matches[3] foreach($matches[2] as $a=>$b) if ($b != "") $matches[3][$a] = $b; // print the whole captured img tags: print_r($matches[1]); // print just the captured paths: print_r($matches[3]);
原输出
Matches:Array ( [0] => Array ( [0] => <img width src = one height> [1] => <img src=two height> [2] => <img width src="three"> [3] => <img src = 'four' /> [4] => <img src =five> ) [1] => Array ( [0] => <img width src = one height> [1] => <img src=two height> [2] => <img width src="three"> [3] => <img src = 'four' /> [4] => <img src =five> ) [2] => Array ( [0] => [1] => [2] => three [3] => four [4] => ) [3] => Array ( [0] => one [1] => two [2] => [3] => [4] => five ) ) Array ( [0] => <img width src = one height> [1] => <img src=two height> [2] => <img width src="three"> [3] => <img src = 'four' /> [4] => <img src =five> ) Array ( [0] => one [1] => two [2] => three [3] => four [4] => five )
优化方案
使用PCRE2支持的分支重置组((?|...)),这个语法可以让不同分支中的捕获组复用同一个编号,从而把带引号和无引号的src值统一捕获到同一个分组里。
优化后的正则表达式:
$regex='/(<IMG(?=\s).*\sSRC\s*=\s* (?| ["\'](.+?)["\'] | (.+?)[\s>] ) (?(?<!>).*?>) )/ix';
优化后代码与输出
$code = <<<EOD 1. <img width src = one height> 2. <notAtStart><img src=two height> 3. NotAtAll 4. <img width src="three"><notAtEnd> 5. <notAtStart><img src = 'four' /><notAtEnd> 6. <img src =five><test> 7. <img WithoutSrc> EOD; $regex='/(<IMG(?=\s).*\sSRC\s*=\s* (?| ["\'](.+?)["\'] | (.+?)[\s>] ) (?(?<!>).*?>) )/ix'; preg_match_all($regex, $code, $matches); echo PHP_EOL . "Matches:"; print_r($matches); // 直接获取统一的src值分组,无需手动复制 echo PHP_EOL . "Captured img tags:"; print_r($matches[1]); echo PHP_EOL . "Captured src values:"; print_r($matches[2]);
输出结果:
Matches:Array ( [0] => Array ( [0] => <img width src = one height> [1] => <img src=two height> [2] => <img width src="three"> [3] => <img src = 'four' /> [4] => <img src =five> ) [1] => Array ( [0] => <img width src = one height> [1] => <img src=two height> [2] => <img width src="three"> [3] => <img src = 'four' /> [4] => <img src =five> ) [2] => Array ( [0] => one [1] => two [2] => three [3] => four [4] => five ) ) Captured img tags:Array ( [0] => <img width src = one height> [1] => <img src=two height> [2] => <img width src="three"> [3] => <img src = 'four' /> [4] => <img src =five> ) Captured src values:Array ( [0] => one [1] => two [2] => three [3] => four [4] => five )
说明
- 分支重置组
(?|...)内部的两个分支,各自的捕获组(.+?)会被视为同一个分组(编号为2),不管匹配的是带引号还是无引号的src值,都会被存入$matches[2]; - 同时保留了原正则对完整
<img>标签的捕获(分组1),满足需求; - 无需再手动复制分组数据,简化了后续处理逻辑。
内容的提问来源于stack exchange,提问作者jamacoe
相关产品推荐
相关产品推荐

