将url转换为字符串中的链接,除非它们位于html标记的属性中

时间:2012-09-21 21:23:44

标签: php regex url preg-replace linkify

我正在尝试从textarea输入($_POST['content'])转换所有要链接的网址。

$content = preg_replace('!(\s|^)((https?://)+[a-z0-9_./?=&-]+)!i', ' <a href="$2" target="_blank">$2</a> ', nl2br($_POST['content'])." ");
$content = preg_replace('!(\s|^)((www\.)+[a-z0-9_./?=&-]+)!i', '<a target="_blank" href="http://$2"  target="_blank">$2</a> ', $content." ");

目标链接格式:www.hello.comhttp(s)://(www).hello.com

但这似乎打破了任何iframe,图像或类似的,

正确的正则表达式如何忽略html标签中的网址?

注意:我知道我需要两个表达方式;一个检测没有协议链接(如www.hello.com,所以我需要预先添加)和另一个检测URL与协议(所以不需要预先添加)。

4 个答案:

答案 0 :(得分:16)

您的代码不应该是iframes等问题,因为在那里,您的网址前面通常有",而不是空格,正如您的模式所需。

但是,这里有不同的解决方案。如果您在HTML评论中有单个<>或类似内容,则可能无法100%有效。但在任何其他情况下,它应该服务你好(我不知道这是否是你的问题)。它使用负前瞻来确保在任何开放>之前没有关闭<(因为这意味着,您在标签内)。

$content = preg_replace('$(\s|^)(https?://[a-z0-9_./?=&-]+)(?![^<>]*>)$i', ' <a href="$2" target="_blank">$2</a> ', $content." ");
$content = preg_replace('$(\s|^)(www\.[a-z0-9_./?=&-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$2"  target="_blank">$2</a> ', $content." ");

如果你不熟悉这种技术,可以进一步详细说明。

(?!        # starts the lookahead assertion; now your pattern will only match, if this subpattern does not match
[^<>]      # any character that is neither < nor >; the > is not strictly necessary but might help for optimization
*          # arbitrary many of those characters (but in a row; so not a single < or > in between)
>          # the closing >
)          # ends the lookahead subpattern

请注意,我更改了正则表达式分隔符,因为我现在正在使用正则表达式中的!

除非您需要第一个子模式(\s|^)用于标记之外的URL,否则您现在也可以删除它(并减少替换中的捕获变量)。

$content = preg_replace('$(https?://[a-z0-9_./?=&-]+)(?![^<>]*>)$i', ' <a href="$1" target="_blank">$1</a> ', $content." ");
$content = preg_replace('$(www\.[a-z0-9_./?=&-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$1"  target="_blank">$1</a> ', $content." ");

最后......你打算不在最后替换包含锚点的URL吗?例如。 www.hello.com/index.html#section1?如果您偶然错过了此项,请将#添加到您允许的网址字符:

$content = preg_replace('$(https?://[a-z0-9_./?=&#-]+)(?![^<>]*>)$i', ' <a href="$1" target="_blank">$1</a> ', $content." ");
$content = preg_replace('$(www\.[a-z0-9_./?=&#-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$1"  target="_blank">$1</a> ', $content." ");

编辑:此外,+%怎么办?还有一些其他字符可以在不进行编码的情况下出现在URL中。 See this. END OF EDIT

我认为这应该适合你。但是,如果您可以提供一个显示工作和损坏URL的示例(使用您拥有的代码),我们实际上可以提供经过测试的解决方案,以适用于您的所有情况。

最后一个想法。适当的解决方案是to use a DOM parser。然后你可以简单地将你已经拥有的正则表达式应用于文本节点。但是,您对HTML结构的关注非常有限,这会使您的问题再次成为常规(只要您在HTML注释或页面上的JavaScript或CSS中没有不匹配的'&lt;'或'&gt;')。如果你确实有这些特殊情况,你应该真正研究一下DOM解析器。在这种情况下,这里提出的解决方案(到目前为止)都不是安全的。

答案 1 :(得分:14)

  1. 在我看来,url是以https?://开头并以空格或行尾(垂直空间或所谓的新行)结尾的所有内容。
  2. 由于第一点,图片,链接等不会被替换,因为它们都以“或&gt;(开头,除非链接<a href=" http...">以空格开头,但这是无效的html )。
  3. 修饰符/m告诉正则表达式匹配每一行(以便第一点中描述的匹配将起作用)。
  4. 替换后应使用函数nl2br(),因为行开头的链接)。
  5. 仅当$ content中最初存在空格时才添加前后空格(在preg_replace()函数的第二个参数中查看$ 1和$ 3)。
  6. 此解决方案支持具有特殊字符的域名,例如www.moški.si
  7. 输入:

    INPUT

    代码:

    <?php
    
    $content =
        preg_replace(
            '~(\s|^)(https?://.+?)(\s|$)~im', 
            '$1<a href="$2" target="_blank">$2</a>$3', 
            $content
        );
    $content = 
        preg_replace(
            '~(\s|^)(www\..+?)(\s|$)~im', 
            '$1<a href="http://$2" target="_blank">$2</a>$3', 
            $content
        );
    $content = nl2br($content);
    

    输出:

    Output

    编辑:

    没有https?://前缀的链接示例+单preg_replace()次调用的示例(模式和替换是数组):

    $content = 
        preg_replace(
            array(
                '~(\s|^)(www\..+?)(\s|$)~im', 
                '~(\s|^)(https?://)(.+?)(\s|$)~im', 
            ),
            array(
                '$1http://$2$3', 
                '$1<a href="$2$3" target="_blank">$3</a>$4', 
            ),
            $content
        );
    $content = nl2br($content);
    

    enter image description here

答案 2 :(得分:3)

让我建议一些不太直接的东西:将输入文本拆分为html和非html部分,然后使用正则表达式处理非html部分,将文本组合成一个部分。水木清华。像:

  <?php
  $chunks = preg_split('/(<.*>)/Ums', $_POST['content'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
  $result = '';
  foreach ($chunks as $chunk) {
    if (substr($chunk,0,1) != '<') {
      /* do your processing on $chunk */
    }
    $result .= $chunk;
  }

一些额外的建议:

  1. 尝试保存源文本并在显示时进行转换。这将允许您在将来找到新的问题/想法时改进/修复渲染代码。
  2. (https?://)+不应该在括号内,你不需要+,因为它匹配“https:// https://some.com” - 只需要把https?:// [一个-Z0-9 _./?=&安培; - ] +
  3. 相同关于(www。)+:)

答案 3 :(得分:3)

此前已经完成了数百次。在这个页面上,m-buettnerglavić工作正常,尽管我喜欢glivic的较短表达。

这是一个很好的php资源: http://code.iamcal.com/php/lib_autolink/

Stackoverflow上的重复:

体面的深度文章: - http://buildinternet.com/2010/05/how-to-automatically-linkify-text-with-php-regular-expressions/