478

我正在使用下面的函数来匹配给定文本中的 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>"); 
}
4

25 回答 25

386

首先,滚动你自己的正则表达式来解析 URL 是一个糟糕的主意。根据RFC ,您必须想象这是一个非常普遍的问题,有人为此编写、调试和测试了一个库。URI 很复杂- 查看Node.js 中的 URL 解析代码和URI 方案的 Wikipedia 页面。

在解析 URL 时有很多边缘情况:国际域名、实际 ( .museum) 与不存在 ( .etc) 顶级域名、奇怪的标点符号(包括括号)、URL 末尾的标点符号、IPV6 主机名等。

我查看了大量库,尽管有一些缺点,但仍有一些值得使用

我很快就取消了这项任务的资格的图书馆:

如果你坚持使用正则表达式,最全面的是来自 Component 的 URL regexp,尽管它会通过查看它来错误地检测到一些不存在的两个字母 TLD。

于 2014-02-21T04:46:07.287 回答
286

用链接替换 ​​URL(一般问题的答案)

问题中的正则表达式错过了很多边缘情况。在检测 URL 时,最好使用专门的库来处理国际域名、新 TLD(如.museumURL 内和 URL 末尾的括号和其他标点符号)以及许多其他极端情况。有关其他一些问题的解释,请参阅 Jeff Atwood 的博客文章URL问题。

URL 匹配库的最佳摘要在Dan Dascalescu 的回答+100
中(截至 2014 年 2 月)


《让一个正则表达式替换多个匹配》(具体问题的解答)

在正则表达式末尾添加“g”以启用全局匹配:

/ig;

但这只能解决正则表达式仅替换第一个匹配项的问题。不要使用该代码。

于 2008-09-01T10:00:52.217 回答
168

我对 Travis 的代码做了一些小的修改(只是为了避免任何不必要的重新声明——但它非常适合我的需要,干得好!):

function linkify(inputText) {
    var replacedText, replacePattern1, replacePattern2, replacePattern3;

    //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links.
    replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText;
}
于 2010-10-08T11:50:19.133 回答
75

Linkify()对上面 Travis 的代码做了一些优化。我还修复了不匹配子域类型格式的电子邮件地址的错误(即 example@domain.co.uk)。

此外,我将实现更改为String类的原型,以便可以像这样匹配项目:

var text = 'address@example.com';
text.linkify();

'http://stackoverflow.com/'.linkify();

无论如何,这是脚本:

if(!String.linkify) {
    String.prototype.linkify = function() {

        // http://, https://, ftp://
        var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;

        // www. sans http:// or https://
        var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;

        // Email addresses
        var emailAddressPattern = /[\w.]+@[a-zA-Z_-]+?(?:\.[a-zA-Z]{2,6})+/gim;

        return this
            .replace(urlPattern, '<a href="$&">$&</a>')
            .replace(pseudoUrlPattern, '$1<a href="http://$2">$2</a>')
            .replace(emailAddressPattern, '<a href="mailto:$&">$&</a>');
    };
}
于 2011-08-19T15:03:50.953 回答
27

谢谢,这很有帮助。我还想要一些能够链接看起来像 URL 的东西的东西——作为基本要求,它会链接像 www.yahoo.com 这样的东西,即使 http:// 协议前缀不存在。所以基本上,如果“www”。存在,它将链接它并假设它是 http://。我还希望电子邮件变成 mailto: 链接。示例:www.yahoo.com 将转换为 www.yahoo.com

这是我最终得到的代码(来自这个页面的代码和我在网上找到的其他东西的组合,以及我自己做的其他东西):

function Linkify(inputText) {
    //URLs starting with http://, https://, or ftp://
    var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    var replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with www. (without // before it, or it'd re-link the ones done above)
    var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    var replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links
    var replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
    var replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText
}

在第二次替换中, (^|[^/]) 部分仅替换 www.whatever.com 如果它还没有前缀 // -- 以避免在第一次替换中已经链接 URL 的情况下进行双重链接。此外,www.whatever.com 可能位于字符串的开头,这是正则表达式该部分中的第一个“或”条件。

这可以集成为一个 jQuery 插件,如上面的 Jesse P 所示——但我特别想要一个不作用于现有 DOM 元素的常规函数​​,因为我正在获取我拥有的文本,然后将其添加到 DOM,并且我希望文本在添加之前被“链接”,所以我通过这个函数传递文本。效果很好。

于 2010-01-29T23:55:58.303 回答
18

识别 URL 很棘手,因为它们通常被标点符号包围,而且用户经常不使用 URL 的完整形式。存在许多用于用超链接替换 ​​URL 的 JavaScript 函数,但我无法找到一个与urlize基于 Python 的 Web 框架 Django 中的过滤器一样有效的函数。因此,我将 Django 的urlize函数移植到 JavaScript:

https://github.com/ljosa/urlize.js

一个例子:

urlize('Go to SO (stackoverflow.com) and ask. <grin>', 
       {nofollow: true, autoescape: true})
=> "Go to SO (<a href="http://stackoverflow.com" rel="nofollow">stackoverflow.com</a>) and ask. &lt;grin&gt;"

第二个参数,如果为真,则导致rel="nofollow"插入。第三个参数,如果为真,转义在 HTML 中具有特殊含义的字符。请参阅自述文件

于 2012-05-08T12:02:45.030 回答
15

我在谷歌上搜索了任何更新的东西并遇到了这个:

$('p').each(function(){
   $(this).html( $(this).html().replace(/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, '<a href="$1">$1</a> ') );
});

演示:http: //jsfiddle.net/kachibito/hEgvc/1/

对于普通链接非常有效。

于 2016-03-24T14:19:11.623 回答
11

我将 Roshambo String.linkify() 更改为 emailAddressPattern 以识别 aaa.bbb.@ccc.ddd 地址

if(!String.linkify) {
    String.prototype.linkify = function() {

        // http://, https://, ftp://
        var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;

        // www. sans http:// or https://
        var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;

        // Email addresses *** here I've changed the expression ***
        var emailAddressPattern = /(([a-zA-Z0-9_\-\.]+)@[a-zA-Z_]+?(?:\.[a-zA-Z]{2,6}))+/gim;

        return this
            .replace(urlPattern, '<a target="_blank" href="$&">$&</a>')
            .replace(pseudoUrlPattern, '$1<a target="_blank" href="http://$2">$2</a>')
            .replace(emailAddressPattern, '<a target="_blank" href="mailto:$1">$1</a>');
    };
}
于 2011-08-21T14:15:09.243 回答
8
/**
 * Convert URLs in a string to anchor buttons
 * @param {!string} string
 * @returns {!string}
 */

function URLify(string){
  var urls = string.match(/(((ftp|https?):\/\/)[\-\w@:%_\+.~#?,&\/\/=]+)/g);
  if (urls) {
    urls.forEach(function (url) {
      string = string.replace(url, '<a target="_blank" href="' + url + '">' + url + "</a>");
    });
  }
  return string.replace("(", "<br/>(");
}

简单的例子

于 2019-04-08T14:21:10.697 回答
7

执行此操作的最佳脚本:http: //benalman.com/projects/javascript-linkify-process-lin/

于 2010-06-25T05:18:20.043 回答
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 支持。

是一个演示。

于 2012-11-22T19:03:47.937 回答
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>");
}
于 2011-12-09T08:42:34.340 回答
4

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

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. 万维网
  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作为有效的通过,只需使用下面的 reg ex: /(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig

于 2014-01-30T11:58:34.870 回答
4

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

于 2016-05-02T18:11:20.303 回答
3

把事情简单化!说你不能拥有的,而不是你能拥有的:)

如上所述,URL 可能非常复杂,尤其是在“?”之后,而且并非所有 URL 都以“www”开头。例如maps.bing.com/something?key=!"£$%^*()&lat=65&lon&lon=20

因此,与其拥有一个无法满足所有边缘情况并且难以维护的复杂正则表达式,不如使用这个更简单的正则表达式,它在实践中对我来说效果很好。

匹配

http(s):// (anything but a space)+

www. (anything but a space)+

'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;
于 2014-05-27T10:58:46.040 回答
3

使用国际域名和星体字符支持进行正确的 URL 检测并非易事。库从许多条件linkify-it构建正则表达式,最终大小约为 6 KB :)。它比目前在接受的答案中引用的所有库更准确。

请参阅linkify-it 演示以实时检查所有边缘案例并测试您的案例。

如果需要链接 HTML 源代码,则应先对其进行解析,然后分别迭代每个文本标记。

于 2015-05-16T19:50:18.747 回答
3

试试下面的功能:

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("Hola amigo! https://www.sharda.ac.in/academics/"));

于 2019-03-12T04:51:08.483 回答
2

我已经编写了另一个 JavaScript 库,它可能对您更好,因为它非常敏感,误报最少,速度快且体积小。我目前正在积极维护它,所以请在演示页面中对其进行测试,看看它对你有什么作用。

链接:https ://github.com/alexcorvi/anchorme.js

于 2016-03-02T21:26:00.123 回答
1

我不得不反其道而行之,将 html 链接仅添加到 URL 中,但我修改了您的正则表达式,它就像一个魅力,谢谢:)

var exp = /<a\s.*href=['"](\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_ |!:,.;]*[-A-Z0-9+&@#\/%=~_|])['"].*>.*<\/a>/ig;

source = source.replace(exp,"$1");
于 2009-04-27T03:20:35.763 回答
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 ”这样的电子邮件地址。

于 2010-02-12T08:02:16.520 回答
1

在从多个来源输入后,我现在有了一个运行良好的解决方案。它与编写自己的替换代码有关。

回答

小提琴

function replaceURLWithHTMLLinks(text) {
    var re = /(\(.*?)?\b((?:https?|ftp|file):\/\/[-a-z0-9+&@#\/%?=~_()|!:,.;]*[-a-z0-9+&@#\/%=~_()|])/ig;
    return text.replace(re, function(match, lParens, url) {
        var rParens = '';
        lParens = lParens || '';

        // Try to strip the same number of right parens from url
        // as there are left parens.  Here, lParenCounter must be
        // a RegExp object.  You cannot use a literal
        //     while (/\(/g.exec(lParens)) { ... }
        // because an object is needed to store the lastIndex state.
        var lParenCounter = /\(/g;
        while (lParenCounter.exec(lParens)) {
            var m;
            // We want m[1] to be greedy, unless a period precedes the
            // right parenthesis.  These tests cannot be simplified as
            //     /(.*)(\.?\).*)/.exec(url)
            // because if (.*) is greedy then \.? never gets a chance.
            if (m = /(.*)(\.\).*)/.exec(url) ||
                    /(.*)(\).*)/.exec(url)) {
                url = m[1];
                rParens = m[2] + rParens;
            }
        }
        return lParens + "<a href='" + url + "'>" + url + "</a>" + rParens;
    });
}
于 2013-11-04T16:59:21.930 回答
1

这是我的解决方案:

var content = "Visit https://wwww.google.com or watch this video: https://www.youtube.com/watch?v=0T4DQYgsazo and news at http://www.bbc.com";
content = replaceUrlsWithLinks(content, "http://");
content = replaceUrlsWithLinks(content, "https://");

function replaceUrlsWithLinks(content, protocol) {
    var startPos = 0;
    var s = 0;

    while (s < content.length) {
        startPos = content.indexOf(protocol, s);

        if (startPos < 0)
            return content;

        let endPos = content.indexOf(" ", startPos + 1);

        if (endPos < 0)
            endPos = content.length;

        let url = content.substr(startPos, endPos - startPos);

        if (url.endsWith(".") || url.endsWith("?") || url.endsWith(",")) {
            url = url.substr(0, url.length - 1);
            endPos--;
        }

        if (ROOTNS.utils.stringsHelper.validUrl(url)) {
            let link = "<a href='" + url + "'>" + url + "</a>";
            content = content.substr(0, startPos) + link + content.substr(endPos);
            s = startPos + link.length;
        } else {
            s = endPos + 1;
        }
    }

    return content;
}

function validUrl(url) {
    try {
        new URL(url);
        return true;
    } catch (e) {
        return false;
    }
}
于 2018-07-24T13:29:33.797 回答
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;
}
于 2019-04-08T09:37:39.877 回答
0

用 HTML 链接替换文本中的 URL,忽略 href/pre 标记中的 URL。 https://github.com/JimLiu/auto-link

于 2015-06-11T21:31:44.743 回答
0

为我工作:

var urlRegex =/(\b((https?|ftp|file):\/\/)?((([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_]*)?)/ig;

return text.replace(urlRegex, function(url) {
    var newUrl = url.indexOf("http") === -1 ? "http://" + url : url;
    return '<a href="' + newUrl + '">' + url + '</a>';
});
于 2021-04-19T15:57:43.580 回答