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

Ton*_*bet 11 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." ");
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Mar*_*der 16

你的代码在iframe中不应该是一个很大的问题,因为在那里你通常有一个"在你的URL前面而不是一个空格,正如你的模式所要求的那样.

但是,这里有不同的解决方案.如果您有单个<>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." ");
Run Code Online (Sandbox Code Playgroud)

如果您不熟悉这种技术,请稍微详细说明.

(?!        # 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
Run Code Online (Sandbox Code Playgroud)

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

除非您需要第一个子模式(\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." ");
Run Code Online (Sandbox Code Playgroud)

最后......你打算不在最后替换包含锚点的URL吗?比如www.hello.com/index.html#section1?如果您偶然错过了此项,请将其添加#到您允许的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." ");
Run Code Online (Sandbox Code Playgroud)

编辑:另外,怎么样+%?还有一些其他字符可以在不进行编码的情况下出现在URL中.看到这个. 编辑结束

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

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


Gla*_*vić 14

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

输入:

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);
Run Code Online (Sandbox Code Playgroud)

输出:

产量

编辑:

没有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);
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 我不在乎赏金!我关心知识.如果我的答案不正确,我想知道为什么.从downvoter那里要问的是多少? (6认同)
  • +1只是为了完美有效的答案. (3认同)