如何将纯文本URL替换为链接?

490

我正在使用以下函数来匹配给定文本中的URL并将它们替换为HTML链接。正则表达式非常有效,但目前我只替换了第一个匹配项。

我应该如何替换所有的URL?我猜我应该使用exec命令,但我真的不知道如何做。

function replaceURLWithHTMLLinks(text) {
    var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/i;
    return text.replace(exp,"<a href='$1'>$1</a>"); 
}
25个回答

6

这个解决方案与其他许多解决方案类似,实际上使用了其中一个解决方案相同的正则表达式,但是不同的是,它返回的不是HTML字符串,而是包含A元素和任何适用的文本节点的文档片段。

 function make_link(string) {
    var words = string.split(' '),
        ret = document.createDocumentFragment();
    for (var i = 0, l = words.length; i < l; i++) {
        if (words[i].match(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi)) {
            var elm = document.createElement('a');
            elm.href = words[i];
            elm.textContent = words[i];
            if (ret.childNodes.length > 0) {
                ret.lastChild.textContent += ' ';
            }
            ret.appendChild(elm);
        } else {
            if (ret.lastChild && ret.lastChild.nodeType === 3) {
                ret.lastChild.textContent += ' ' + words[i];
            } else {
                ret.appendChild(document.createTextNode(' ' + words[i]));
            }
        }
    }
    return ret;
}

需要注意的是,旧版IE和textContent支持可能存在一些问题。

这里有一个演示。


2
@DanDascalescu,与其一概而论地对所有内容进行负面评价,不如提供你所说的边缘情况。 - rlemon
2
所以有一些边缘情况。太好了。这些答案仍然可能对其他人有用,而全盘否定它们似乎有些过头了。你评论并貌似给出了负面评价的其他答案确实包含有用的信息(以及你的答案)。并非每个人都会遇到这些情况,也不是每个人都想使用库。 - rlemon
没错。那些不理解正则表达式限制的人会很高兴地浏览最受欢迎答案中的第一个正则表达式并使用它。这些人应该最多使用库。 - Dan Dascalescu
1
但这是否足以证明您可以对每个回答进行负面评价,只因其不是您偏爱的解决方案之一的正则表达式呢? - rlemon
为了让一个真正有用的答案浮现到顶部。人们的注意力很短,而“选择的悖论”表明他们会在第N个答案之后停止寻找答案。 - Dan Dascalescu

5
如果您需要显示更短的链接(仅域名),但具有相同的长URL,则可以尝试我的修改版Sam Hasler的代码版本,该版本已发布如上所述。
function replaceURLWithHTMLLinks(text) {
    var exp = /(\b(https?|ftp|file):\/\/([-A-Z0-9+&@#%?=~_|!:,.;]*)([-A-Z0-9+&@#%?\/=~_|!:,.;]*)[-A-Z0-9+&@#\/%=~_|])/ig;
    return text.replace(exp, "<a href='$1' target='_blank'>$3</a>");
}

4

正则表达式:/(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]*)/ig

这个正则表达式可以用来匹配URL链接。它可以识别以http、https、ftp、file或www开头的任何URL,并且可以包含各种字符,如字母、数字、符号等。

function UriphiMe(text) {
      var exp = /(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]*)/ig; 
      return text.replace(exp,"<a href='$1'>$1</a>");
}

以下是一些已测试的字符串:

  1. 在 www.google.com 上找到我
  2. www
  3. 在 www.http://www.com 上找到我
  4. 关注我:http://www.nishantwork.wordpress.com
  5. http://www.nishantwork.wordpress.com
  6. 关注我:http://www.nishantwork.wordpress.com
  7. https://stackoverflow.com/users/430803/nishant

注意:如果您不想将 www 作为有效内容,请使用以下正则表达式:/(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig


上面的代码在处理边缘情况时会失败很多测试。在检测URL时,最好始终依赖于专门的库。这就是为什么 - Dan Dascalescu

4

需要注意关于URI复杂性的警告,但是回答你的问题很简单:
要替换每个匹配项,您需要在正则表达式的末尾添加/g标志:
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi


4

尝试使用以下函数:

function anchorify(text){
  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
  var text1=text.replace(exp, "<a href='$1'>$1</a>");
  var exp2 =/(^|[^\/])(www\.[\S]+(\b|$))/gim;
  return text1.replace(exp2, '$1<a target="_blank" href="http://$2">$2</a>');
}

alert(anchorify("你好朋友!https://www.sharda.ac.in/academics/"));

该代码段是一个JavaScript函数,它使用anchorify()函数将URL链接转换为HTML锚点,并在警告框中显示文本“你好朋友!”。

1
适用于 https:// https://www. http:// http://www. www.,效果很好。 - Linesofcode

3
保持简单!说出你不能拥有的,而不是你可以拥有的 :)
如上所述,URL可能非常复杂,特别是在“?”之后,并且并非所有URL都以“www。”开头,例如maps.bing.com/something?key=!"£$%^*()&lat=65&lon&lon=20 因此,不要使用复杂的正则表达式,这些正则表达式无法满足所有边缘情况,并且很难维护,而是使用这个更简单的正则表达式,在实践中效果很好。
匹配: http(s):// (除空格外的任何字符)+ www. (除空格外的任何字符)+ 其中“anything”是[^'"<>\s],基本上是贪婪匹配,一直匹配到遇到空格、引号、尖括号或行尾为止。
另外:
记得检查它是否已经处于URL格式,例如文本包含href="..."src="..." 添加ref=nofollow(如果适用)
这个解决方案不像上面提到的库那么“好”,但更简单,实践中效果很好。
if html.match( /(href)|(src)/i )) {
    return html; // text already has a hyper link in it
    }

html = html.replace( 
            /\b(https?:\/\/[^\s\(\)\'\"\<\>]+)/ig, 
            "<a ref='nofollow' href='$1'>$1</a>" 
            );

html = html.replace( 
            /\s(www\.[^\s\(\)\'\"\<\>]+)/ig, 
            "<a ref='nofollow' href='http://$1'>$1</a>" 
            );

html = html.replace( 
             /^(www\.[^\s\(\)\'\"\<\>]+)/ig, 
            "<a ref='nofollow' href='http://$1'>$1</a>" 
            );

return html;

3
正确识别国际域名和星体字符的URL并不是一件简单的事情。linkify-it库从许多条件中构建正则表达式,最终大小约为6千字节 :). 它比所有当前被引用的库更准确,这在接受的答案中已经有所体现。
请查看linkify-it演示以检查所有边缘情况并测试您自己的情况。
如果您需要链接化HTML源代码,则应首先解析它,并单独迭代每个文本标记。

2
我写了一个新的JavaScript库,它可能更适合你,因为它非常敏感,几乎没有误报,速度快,体积小。我目前正在积极维护它,所以请务必测试一下演示页面,看看它是否适合你。
链接:https://github.com/alexcorvi/anchorme.js

@Alex C 的 npm 包叫什么名字? - Marcus

1

Travitron 上面的电子邮件检测对我不起作用,所以我用以下代码进行了扩展/替换(C# 代码)。

// Change e-mail addresses to mailto: links.
const RegexOptions o = RegexOptions.Multiline | RegexOptions.IgnoreCase;
const string pat3 = @"([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,6})";
const string rep3 = @"<a href=""mailto:$1@$2.$3"">$1@$2.$3</a>";
text = Regex.Replace(text, pat3, rep3, o);

这使得电子邮件地址可以像“firstname.secondname@one.two.three.co.uk”这样。

1
上面的代码在处理边缘情况时会失败很多测试。在检测URL时,最好始终依赖于专门的库。这就是为什么 - Dan Dascalescu
谢谢,@DanDascalescu 通常来说,过度泛化总是更好的选择。 - Uwe Keim

1
尝试以下解决方案。
function replaceLinkClickableLink(url = '') {
let pattern = new RegExp('^(https?:\\/\\/)?'+
        '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|'+
        '((\\d{1,3}\\.){3}\\d{1,3}))'+
        '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+
        '(\\?[;&a-z\\d%_.~+=-]*)?'+
        '(\\#[-a-z\\d_]*)?$','i');

let isUrl = pattern.test(url);
if (isUrl) {
    return `<a href="${url}" target="_blank">${url}</a>`;
}
return url;
}

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