从字符串中提取URL

17

我试图找到一种可靠的方法来从字符串中提取URL。 我有一个网站,用户回答问题并在源框中输入信息来源时,我允许他们输入URL。 我想提取该URL并将其变成超链接。 就像Yahoo Answers所做的那样。

有人知道可以做到这一点的可靠解决方案吗?

我找到的所有解决方案都适用于某些URL,但不适用于其他URL。

谢谢

5个回答

22

John Gruber花了相当多的时间来完善用于链接检测的“一条正则表达式统治它们所有”的方法。使用preg_replace(),如其他答案中所提到的,使用以下正则表达式应该是最准确的方法之一,如果不是最准确的方法,则是最准确的方法之一:

(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))

如果你只想匹配HTTP/HTTPS:

(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))

4
\b(?:(?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|((?:[^\s()<>]+|(?:([^\s()<>]+)))*))+(?:((?:[^\s()<>]+|(?:([^\s()<>]+)))*)|[^\s`!()[]{};:'".,<>?«»“”‘’])),对于任何想要将所有子模式转换为非捕获模式并转义正斜杠的人: - Highly Irregular
TLD可能超过4个字符,请参见:http://www.iana.org/domains/root/db - Toto
3
我们该如何在 preg 中使用这个正则表达式?我的意思是,因为它包含双引号和单引号,所以代码无法正常工作,例如:preg_match('(?i)\b......]))', $str) - 整段代码似乎都被注释了。 - Linesofcode
不起作用。Preg_match和preg_match_all每次都失败,即使删除了单引号/双引号。 - Aakash Sahai

3
$string = preg_replace('/https?:\/\/[^\s"<>]+/', '<a href="$0" target="_blank">$0</a>', $string);

它只匹配http / https,但那确实是您想要转换为链接的唯一协议。 如果您想要其他协议,可以像这样更改:

$string = preg_replace('/(https?|ssh|ftp):\/\/[^\s"]+/', '<a href="$0" target="_blank">$0</a>', $string);

1
您可能还想要排除 < 或对匹配的字符串应用 htmlspecialchars 以避免代码注入。 - Gumbo
不错,但是如果你看一下这个表达式,它可以接受除了空格和引号之外的任何字符。我相信这样就排除了任何HTML注入的可能性。 - Jonah
1
来源:不,您不仅将匹配的值用作属性值,还将其用作元素文本内容。 - Gumbo

2

URL存在很多特殊情况。例如,URL可能包含括号或不包含协议等。这就是为什么正则表达式不够用的原因。

我创建了一个PHP库,可以处理许多边缘情况:Url highlight

您可以从字符串中提取URL或直接突出显示它们。
示例:

<?php

use VStelmakh\UrlHighlight\UrlHighlight;

$urlHighlight = new UrlHighlight();

// Extract urls
$urlHighlight->getUrls("This is example http://example.com.");
// return: ['http://example.com']

// Make urls as hyperlinks
$urlHighlight->highlightUrls('Hello, http://example.com.');
// return: 'Hello, <a href="http://example.com">http://example.com</a>.'

更多细节请参见自述文件。有关覆盖的URL情况,请参见测试


0

Yahoo! Answers 在链接正确书写且与其他文本分开时,能够相当好地识别链接,但在分离尾随标点方面表现不佳。例如,The links are http://example.com/somepage.php, http://example.com/somepage2.php, and http://example.com/somepage3.php. 将在前两个链接中包含逗号,在第三个链接中包含句号。

但如果这是可以接受的,那么像这样的模式应该可以解决问题:

\<http:[^ ]+\>

看起来stackoverflow的解析器更好。它是开源的吗?


更智能,但仍不完美。会错过像ssh+svn这样的东西。 - DampeS8N

-1

这段代码对我来说是有效的。

function makeLink($string){

/*** make sure there is an http:// on all URLs ***/
$string = preg_replace("/([^\w\/])(www\.[a-z0-9\-]+\.[a-z0-9\-]+)/i", "$1http://$2",$string);
/*** make all URLs links ***/
$string = preg_replace("/([\w]+:\/\/[\w-?&;#~=\.\/\@]+[\w\/])/i","<a target=\"_blank\" href=\"$1\">$1</a>",$string);
/*** make all emails hot links ***/
$string = preg_replace("/([\w-?&;#~=\.\/]+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?))/i","<a href=\"mailto:$1\">$1</a>",$string);

return $string;
}

1
你为什么将顶级域名限制在3个字符内?请查看:http://www.iana.org/domains/root/db - Toto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接