使用 JavaScript 检测文本中的 URL

IT技术 javascript regex url
2021-02-01 22:34:52

有没有人建议检测一组字符串中的 URL?

arrayOfStrings.forEach(function(string){
  // detect URLs in strings and do something swell,
  // like creating elements with links.
});

更新:我最终使用这个正则表达式进行链接检测……显然是几年后。

kLINK_DETECTION_REGEX = /(([a-z]+:\/\/)?(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(\s+|$)/gi

完整的帮助程序(带有可选的 Handlebars 支持)位于gist #1654670

6个回答

首先,您需要一个匹配 url 的好的正则表达式。这很难做到。请参阅此处此处此处

...几乎任何东西都是有效的 URL。有一些标点符号规则可以拆分它。没有任何标点符号,您仍然有一个有效的 URL。

仔细检查 RFC,看看您是否可以构造一个“无效”的 URL。规则非常灵活。

例如:::::是一个有效的 URL。路径是":::::"一个非常愚蠢的文件名,但一个有效的文件名。

此外,/////是一个有效的 URL。netloc(“主机名”)是"". 路径是"///"又是笨蛋。也有效。此 URL 标准化为"///" 等效的。

类似的东西"bad://///worse/////" 是完全有效的。愚蠢但有效。

无论如何,这个答案并不是要为您提供最好的正则表达式,而是要证明如何使用 JavaScript 在文本中进行字符串换行。

好的,让我们使用这个: /(https?:\/\/[^\s]+)/g

同样,这是一个糟糕的正则表达式它会有很多误报。但是对于这个例子来说已经足够了。

function urlify(text) {
  var urlRegex = /(https?:\/\/[^\s]+)/g;
  return text.replace(urlRegex, function(url) {
    return '<a href="' + url + '">' + url + '</a>';
  })
  // or alternatively
  // return text.replace(urlRegex, '<a href="$1">$1</a>')
}

var text = 'Find me at http://www.example.com and also at http://stackoverflow.com';
var html = urlify(text);

console.log(html)

// html now looks like:
// "Find me at <a href="http://www.example.com">http://www.example.com</a> and also at <a href="http://stackoverflow.com">http://stackoverflow.com</a>"

所以总而言之,请尝试:

$$('#pad dl dd').each(function(element) {
    element.innerHTML = urlify(element.innerHTML);
});
这很好,但它text="Find me at http://www.example.com, and also at http://stackoverflow.com."在两个 404 的尾随标点符号结果中做了“错误”的事情一些用户意识到了这一点,并且会在标点符号之前的 URL 之后添加一个空格以避免损坏,但我使用的大多数链接器(Gmail、etherpad、phabricator)将尾随标点符号与 URL 分开。
2021-03-10 22:34:52
如果文本已包含锚定 url,则您正在使用 jquery 删除锚点,但我使用的是 Angular。如何删除 Angular 中的锚点?
2021-03-27 22:34:52
我从来不知道你可以将函数作为第二个参数传递给.replace:|
2021-03-28 22:34:52
“许多误报”的一些例子将大大改善这个答案。否则,未来的 Google 员工只会留下一些(可能有效?)FUD。
2021-04-02 22:34:52
如果文本已经包含锚定 url,您可以使用 function removeAnchors(text) { var div = $('<div></div>').html(text); div.find('a').contents().unwrap(); 返回 div.text(); 在返回 text.replace 之前首先删除锚点
2021-04-02 22:34:52

这是我最终用作正则表达式的内容:

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

这不包括 URL 中的尾随标点符号。Crescent 的功能就像一个魅力:) 所以:

function linkify(text) {
    var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
    return text.replace(urlRegex, function(url) {
        return '<a href="' + url + '">' + url + '</a>';
    });
}
简单又好看!但是urlRegex应该在外面 定义,linkify因为编译它很昂贵。
2021-03-25 22:34:52
我添加()了每个字符列表,现在可以使用了。
2021-03-25 22:34:52
这无法检测到完整的 URL:disney.wikia.com/wiki/Pua_(Moana)
2021-03-27 22:34:52
最后一个在最明显的情况下真正有效的正则表达式!这个值得收藏。我从谷歌搜索中测试了数千个例子,直到我找到这个。
2021-04-03 22:34:52
它无法检测到仅以 www 开头的 url。例如:www.facebook.com
2021-04-06 22:34:52

我在谷歌上搜索了这个问题很长一段时间,然后我突然想到有一个 Android 方法,android.text.util.Linkify,它利用一些非常强大的正则表达式来完成这个。幸运的是,Android 是开源的。

他们使用几种不同的模式来匹配不同类型的 url。你可以在这里找到它们:http : //grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.0_r1/android/text/util/Regex.java#Regex。 0WEB_URL_PATTERN

如果您只关心与 WEB_URL_PATTERN 匹配的 url,即符合 RFC 1738 规范的 url,您可以使用:

/((?:(http|https|Http|Https|rtsp|Rtsp):\/\/(?:(?:[a-zA-Z0-9\$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,64}(?:\:(?:[a-zA-Z0-9\$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,25})?\@)?)?((?:(?:[a-zA-Z0-9][a-zA-Z0-9\-]{0,64}\.)+(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnrwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eouw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw]))|(?:(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\:\d{1,5})?)(\/(?:(?:[a-zA-Z0-9\;\/\?\:\@\&\=\#\~\-\.\+\!\*\'\(\)\,\_])|(?:\%[a-fA-F0-9]{2}))*)?(?:\b|$)/gi;

以下是源码全文:

"((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+ "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+ "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+ "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+"   // named host
+ "(?:"   // plus top level domain
+ "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+ "|(?:biz|b[abdefghijmnorstvwyz])"
+ "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+ "|d[ejkmoz]"
+ "|(?:edu|e[cegrstu])"
+ "|f[ijkmor]"
+ "|(?:gov|g[abdefghilmnpqrstuwy])"
+ "|h[kmnrtu]"
+ "|(?:info|int|i[delmnoqrst])"
+ "|(?:jobs|j[emop])"
+ "|k[eghimnrwyz]"
+ "|l[abcikrstuvy]"
+ "|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
+ "|(?:name|net|n[acefgilopruz])"
+ "|(?:org|om)"
+ "|(?:pro|p[aefghklmnrstwy])"
+ "|qa"
+ "|r[eouw]"
+ "|s[abcdeghijklmnortuvyz]"
+ "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+ "|u[agkmsyz]"
+ "|v[aceginu]"
+ "|w[fs]"
+ "|y[etu]"
+ "|z[amw]))"
+ "|(?:(?:25[0-5]|2[0-4]" // or ip address
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
+ "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ "|[1-9][0-9]|[0-9])))"
+ "(?:\\:\\d{1,5})?)" // plus option port number
+ "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
+ "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+ "(?:\\b|$)";

如果你真的想变得很花哨,你也可以测试电子邮件地址。电子邮件地址的正则表达式是:

/[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}\\@[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25})+/gi

PS:上述正则表达式支持的顶级域截至 2007 年 6 月是最新的。有关最新列表,您需要查看https://data.iana.org/TLD/tlds-alpha-by-domain.txt .

这在 101% 的情况下都有效,不幸的是,它还可以找到前面没有空格的网址。如果我在 hello@mydomain.com 上进行匹配,它会捕获“mydomain.com”。有没有办法对此进行改进,以便仅在它前面有空格时才能捕获它?
2021-03-10 22:34:52
请注意,grepcode.com 已不再可用,是我认为指向 Android 源代码中正确位置的链接。我认为 Android 使用的正则表达式可能自 2013 年(原始帖子)以来已更新,但自 2015 年以来似乎没有更新,因此可能缺少一些较新的 TLD。
2021-03-14 22:34:52
还要注意,这非常适合捕捉用户输入的网址
2021-03-26 22:34:52
这很好,但我不确定我是否会使用它。对于大多数用例,我宁愿接受一些误报,也不愿使用依赖于硬编码 TLD 列表的方法。如果您在代码中列出 TLD,则可以保证它总有一天会过时,如果可以避免的话,我宁愿不将强制性的未来维护构建到我的代码中。
2021-04-01 22:34:52
由于您有一个不区分大小写的正则表达式,因此您不必指定a-zA-Zhttp|https|Http|Https|rtsp|Rtsp
2021-04-05 22:34:52

基于Crescent Fresh 的答案

如果您想检测带有 http://不带 http:// 和 www 的链接。你可以使用以下

function urlify(text) {
    var urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
    //var urlRegex = /(https?:\/\/[^\s]+)/g;
    return text.replace(urlRegex, function(url,b,c) {
        var url2 = (c == 'www.') ?  'http://' +url : url;
        return '<a href="' +url2+ '" target="_blank">' + url + '</a>';
    }) 
}
这是一个很好的解决方案,但我还想检查文本中不应该已经包含 href 。我试过这个正则表达式 = /((?!href)((https?:\/\/)|(www\.)|(mailto:))[^\s]+)/gi 但它不起作用。你能帮我解决这个问题,或者为什么上面的正则表达式不起作用。
2021-03-13 22:34:52
这将匹配像 www.xyz 这样的无价 url
2021-03-16 22:34:52
我喜欢你还在返回的输出中添加了 target="_blank"。这个版本是我想要的。没有什么太重要的(否则我会使用 Linkifyjs)就足以获得大多数链接。
2021-04-05 22:34:52

NPM 上的这个库看起来非常全面https://www.npmjs.com/package/linkifyjs

Linkify 是一个小而全面的 JavaScript 插件,用于查找纯文本 URL 并将它们转换为 HTML 链接。它适用于所有有效的 URL 和电子邮件地址。

我刚刚在我的项目中实现了 linkifyjs,这太棒了。Linkifyjs 应该是这个问题的答案。另一个要查看的是github.com/twitter/twitter-text
2021-03-28 22:34:52