【问题标题】:preg_match_all and foreach only replacing last matchpreg_match_all 和 foreach 只替换最后一个匹配
【发布时间】:2015-10-16 22:54:21
【问题描述】:

我有以下代码,它应该使纯文本链接可点击。但是,如果有多个链接,它只会替换最后一个。

代码:

$nc = preg_match_all('#<pre[\s\S]*</pre>#U', $postbits, $matches_code); 
foreach($matches_code[0] AS $match_code) 
{
    $match = null;
    $matches = null;
    $url_regex = '#https?://(\w*:\w*@)?[-\w.]+(:\d+)?(/([\w/_.]*(\?\S+)?)?)?[^<\.,:;"\'\s]+#'; 
    $n = preg_match_all($url_regex, $match_code, $matches);
    foreach($matches[0] AS $match)
    {
        $html_url = '<a href="' . $match . '" target="_blank">' . $match . '</a>';
        $match_string = str_replace($match, $html_url, $match_code);
    }
    $postbits = str_replace($match_code, $match_string, $postbits); 
}

结果:

http://www.google.com

http://www.yahoo.com

http://www.microsoft.com/ <-- only this one is clickable

预期结果:

http://www.google.com

http://www.microsoft.com/

我的错误在哪里?

【问题讨论】:

  • [\s\S] 的意义何在?这匹配所有内容,就像.。如果问题是它不匹配换行符,请使用 s 修饰符。

标签: php foreach preg-match-all


【解决方案1】:

如果有多个链接,它只会替换最后一个

我的错误在哪里?

实际上是替换了所有 3 个链接,但每次都替换原来的字符串。

foreach($matches[0] AS $match)
{
    $html_url = '<a href="' . $match . '" target="_blank">' . $match . '</a>';
    $match_string = str_replace($match, $html_url, $match_code);
}

循环执行 3 次,每次替换 $match_code 中的 1 个链接并将结果分配给 $match_string。在第一次迭代中,$match_string 被分配了带有 可点击 google.com 的结果。在第二次迭代中,$match_string 被分配了一个可点击 yahoo.com。但是,您刚刚替换了原始字符串,所以 google.com 现在不能点击。这就是为什么您只能获得最后一个链接的原因。


您可能还想在代码中更正几件事:

  1. 正则表达式#&lt;pre[\s\S]*&lt;/pre&gt;#U 最好构造为#&lt;pre.*&lt;/pre&gt;#Us[\s\S]* 类通常用于 JavaScript,其中没有 s 标志来允许点匹配换行符。
  2. 我不明白您为什么要使用该模式来匹配 URL。我认为您可以简单地使用https?://\S+。我还会将您链接到一些替代方案 here
  3. 您正在使用 2 个 preg_match_all() 调用和 1 个 str_replace() 调用来获取相同的文本,您可以将其包含在 1 个 preg_replace() 中。

代码

$postbits = "
<pre>
http://www.google.com

http://w...content-available-to-author-only...o.com

http://www.microsoft.com/ <-- only this one clickable
</pre>";


$regex = '#\G((?:(?!\A)|.*<pre)(?:(?!</pre>).)*)(https?://\S+?)#isU';
$repl = '\1<a href="\2" target="_blank">\2</a>';

$postbits = preg_replace( $regex, $repl, $postbits);

ideone demo

正则表达式

  • \G 始终从主题中的第一个匹配位置开始。
  • 第 1 组
    • (?:(?!\A)|.*&lt;pre) 匹配字符串开头的第一个 &lt;pre 标签,如果在此标签中找不到更多 URL,则允许获取 下一个 &lt;pre 标签。
    • (?:(?!&lt;/pre&gt;).)*) 使用 &lt;pre&gt; 标记内的任何字符。
  • 第 2 组
    • (https?://\S+?) 匹配 1 个网址。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-14
    • 2014-12-26
    • 1970-01-01
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多