PHP环境下如何编写Perl正则匹配无前缀等号与引号的URL
解决PHP正则匹配无等号前缀URL的编译错误
你的问题出在PHP的PCRE引擎对**负向预查(lookbehind)**的限制:旧版本PHP不支持可变长度的lookbehind断言,你写的(?<!\s*=\s*['"]?\s*)里包含\s*(匹配0到任意多个空白),属于可变长度模式,导致正则编译失败。下面给出几种可行的解决方法,优先匹配你需要的http://www.url1.com/和http://www.url3.com/:
方案1:PHP 7.3+ 用有限长度lookbehind修复(推荐)
PHP 7.3及以上的PCRE2引擎支持有限长度的lookbehind,只要把可变长度的\s*换成固定最大长度的\s{0,10}(可根据实际需求调整最大空白数),就能直接修复原正则:
$html = " http://www.url1.com/ = ' http://www.url2.com/ 'http://www.url3.com/ <a href='http://www.url4.com/'>Testing1</a> <img src='https://url5.com'>Testing2</a>"; $url_pregex = '((http(s)?://)[-a-zA-Z()0-9@:%_+.~#?&;//=]+)'; // 将\s*替换为\s{0,10},限制空白最多10个,满足有限长度要求 $pregex = '(?<!\s{0,10}=\s{0,10}[\'"]?\s{0,10})'.$url_pregex; preg_match_all('`'.$pregex.'`i', $html, $matches); echo "Matches<br><pre>"; var_export($matches); echo "</pre>";
方案2:兼容低版本PHP的替代方案
如果你的PHP版本低于7.3,可以用以下两种方式绕过可变长度lookbehind的限制:
方法A:先匹配所有URL,再过滤不符合的
先提取所有URL,再通过后处理排除掉前面带有等号(含可选引号、空白)的URL:
$html = " http://www.url1.com/ = ' http://www.url2.com/ 'http://www.url3.com/ <a href='http://www.url4.com/'>Testing1</a> <img src='https://url5.com'>Testing2</a>"; $url_pregex = '((http(s)?://)[-a-zA-Z()0-9@:%_+.~#?&;//=]+)'; preg_match_all('`'.$url_pregex.'`i', $html, $matches); // 过滤规则:URL前不能是等号+可选引号+空白结尾的内容 $valid_matches = array_filter($matches[0], function($url) use ($html) { $pos = strpos($html, $url); if ($pos === 0) return true; $prefix = substr($html, 0, $pos); return !preg_match('`\s*=\s*[\'"]?\s*$`', $prefix); }); echo "Matches<br><pre>"; var_export(array_values($valid_matches)); echo "</pre>";
方法B:用正向预查+\K重置匹配起点
利用\K丢弃前面的匹配内容,只捕获符合条件的URL,同时通过正向预查确保前面的内容不包含排除前缀:
$html = " http://www.url1.com/ = ' http://www.url2.com/ 'http://www.url3.com/ <a href='http://www.url4.com/'>Testing1</a> <img src='https://url5.com'>Testing2</a>"; // 匹配任意字符直到URL,同时确保前面没有等号前缀,用\K只保留URL部分 $pregex = '`(?:^|(?!\s*=\s*[\'"]?\s*).)*?\K((http(s)?://)[-a-zA-Z()0-9@:%_+.~#?&;//=]+)`i'; preg_match_all($pregex, $html, $matches); echo "Matches<br><pre>"; var_export($matches[1]); echo "</pre>";
最终效果
以上所有方案都会正确匹配到目标URL:
http://www.url1.com/http://www.url3.com/
并排除掉属于HTML属性(href/src)或带有等号前缀的URL。
内容的提问来源于stack exchange,提问作者developer981
相关产品推荐
相关产品推荐

