PHP Regex / str_replace奇怪的不匹配

时间:2016-03-01 22:42:02

标签: php regex html-parsing

这个让我感到困惑,我似乎无法弄清楚为什么http://www.example.com/a/b/c会返回https://example.net//b/c - 最好的猜测是它与第一场比赛相冲突,但为什么呢?

代码:

 $contents = '
<a href="http://www.example.com/a">Works</a>
<a href="http://www.example.com/a/b/c">Doesnt Work</a>
<a href="http://www.example.com/x/y/z">Works</a>';


            $regexp = "/<a\s[^>]*href=\"([^\"]*)\"[^>]*>(.*)<\/a>/siU";
            if(preg_match_all($regexp, $contents, $matches, PREG_SET_ORDER)) {
                foreach($matches as $match) {
                    print_r($match);
                    if (!empty($match[1])) { 
                        $urlString = 'https://www.example.net/newlink/';
                        $contents = str_replace($match[1], $urlString, $contents);
                    }
                }
            }

echo $contents;

输出:

Array
(
    [0] => <a href="http://www.example.com/a">Works</a>
    [1] => http://www.example.com/a
    [2] => Works
)
Array
(
    [0] => <a href="http://www.example.com/a/b/c">Doesnt Work</a>
    [1] => http://www.example.com/a/b/c
    [2] => Doesnt Work
)
Array
(
    [0] => <a href="http://www.example.com/x/y/z">Works</a>
    [1] => http://www.example.com/x/y/z
    [2] => Works
)

    <a href="https://www.example.net/newlink/">Works</a>
    <a href="https://www.example.net/newlink//b/c">Doesnt Work</a>
    <a href="https://www.example.net/newlink/">Works</a>

https://eval.in/528426

2 个答案:

答案 0 :(得分:0)

请参阅str_replace()

的手册

它用http://www.example.com/a替换了https://www.example.net/newlink/的两次出现 然后它无法找到http://www.example.com/a/b/c,因为此时https://www.example.net/newlink//b/c

修改:这应该有效:$contents = str_replace('"'.$match[1].'"', '"'.$urlString.'"', $contents); //在搜索/替换中包含引号

答案 1 :(得分:0)

问题是在第一次迭代期间在$contents中执行了2次替换,因为有2个http://www.example.com/a子字符串。

一种可能的解决方案是使用preg_replace_callback来匹配捕获您需要保留的所有部分的子字符串,并仅匹配您需要替换的内容:

请参阅IDEONE demo

$contents = '<a href="http://www.example.com/a">Works</a>
<a href="http://www.example.com/a/b/c">Doesnt Work</a>
<a href="http://www.example.com/x/y/z">Works</a>';
$regexp = "/(<a\s[^>]*href=\")[^\"]*(\"[^>]*>.*<\/a>)/siU";
$contents = preg_replace_callback($regexp, function($m) {
  return $m[1] . 'https://www.example.net/newlink/' . $m[2];
}, $contents);
echo $contents;

但是,如果您正在处理HTML,我宁愿使用基于DOM的解决方案。 以下是如何将所有链接设置为指向https://www.example.net/newlink/

$html = <<<DATA
<a href="http://www.example.com/a">Works</a>
<a href="http://www.example.com/a/b/c">Doesnt Work</a>
<a href="http://www.example.com/x/y/z">Works</a>
DATA;

$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$xpath = new DOMXPath($dom);
$links = $xpath->query('//a');

foreach($links as $link) { 
   $link->setAttribute('href', 'https://www.example.net/newlink/');
}
echo $dom->saveHTML();

请参阅another demo