我正在尝试从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.com
或http(s)://(www).hello.com
但这似乎打破了任何iframe,图像或类似的,
正确的正则表达式如何忽略html标签中的网址?
注意:我知道我需要两个表达方式;一个检测没有协议链接(如www.hello.com
,所以我需要预先添加)和另一个检测URL与协议(所以不需要预先添加)。
答案 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)
https?://
开头并以空格或行尾(垂直空间或所谓的新行)结尾的所有内容。<a href=" http...">
以空格开头,但这是无效的html )。/m
告诉正则表达式匹配每一行(以便第一点中描述的匹配将起作用)。nl2br()
(,因为行开头的链接)。
<?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);
没有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);
答案 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;
}
一些额外的建议:
答案 3 :(得分:3)
此前已经完成了数百次。在这个页面上,m-buettner和glavić工作正常,尽管我喜欢glivic的较短表达。
这是一个很好的php资源: http://code.iamcal.com/php/lib_autolink/
Stackoverflow上的重复:
体面的深度文章: - http://buildinternet.com/2010/05/how-to-automatically-linkify-text-with-php-regular-expressions/