我正在使用下面的函数来匹配给定文本中的URL,并将其替换为HTML链接。正则表达式工作得很好,但目前我只是替换了第一场比赛。
如何更换所有网址?我想我应该使用 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>");
}
答案 0 :(得分:320)
首先,滚动自己的正则表达式来解析网址是一个糟糕的主意。根据{{3}},你必须想象这是一个常见的问题,有人为它编写,调试和tested库。 URI很复杂 - 查看the RFCs上的code for URL parsing in Node.js和维基百科页面。
在解析网址方面存在大量优势:URI schemes,实际(.museum
)与不存在(.etc
)TLD,包括{{3}在内的奇怪标点符号},URL末尾的标点符号,IPV6主机名等。
我看过international domain names的parentheses,除了一些缺点外,还有一些值得使用:
href
属性”)。当IDN issues。我很快就取消了这项任务的资格:
如果你坚持使用正则表达式,那么最全面的是autolink-js,但是通过查看它会错误地检测到一些不存在的双字母TLD。
答案 1 :(得分:284)
问题中的正则表达式错过很多的边缘情况。在检测URL时,最好使用专门的库来处理国际域名,新的顶级域名(如.museum
),括号和URL末尾的其他标点符号以及许多其他边缘情况。有关其他一些问题的解释,请参阅Jeff Atwood的博客文章 The Problem With URLs 。
网址匹配库的最佳摘要位于 Dan Dascalescu's Answer
(截至2014年2月)
在正则表达式的末尾添加“g”以启用全局匹配:
/ig;
但这只能解决正则表达式只替换第一个匹配的问题。 不要使用该代码。
答案 2 :(得分:145)
我对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;
}
答案 3 :(得分:67)
对上面的Travis'Linkify()
代码进行了一些优化。我还修复了一个错误,即子域类型格式的电子邮件地址不匹配(即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>');
};
}
答案 4 :(得分:23)
谢谢,这非常有帮助。我还想要一些可以链接看似URL的东西的东西 - 作为一个基本要求,即使http://协议前缀不存在,它也会像www.yahoo.com这样链接。所以基本上,如果“www。”它存在,它将链接它并假设它是http://。我还希望电子邮件变成mailto:links。示例: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,如果它还没有前缀为// - 以避免在第一次链接已经链接的情况下进行双重链接更换。此外,www.whatever.com可能位于字符串的开头,这是正则表达式中该部分的第一个“或”条件。
这可以作为jQuery插件集成,如上面所示的Jesse P - 但我特别想要一个不在现有DOM元素上运行的常规函数,因为我正在使用我拥有的文本然后将其添加到DOM,我想在添加之前将文本“链接”,所以我通过这个函数传递文本。效果很好。
答案 5 :(得分:17)
识别URL很棘手,因为它们通常被标点符号包围,并且因为用户经常不使用完整形式的URL。存在许多用于替换带有超链接的URL的JavaScript函数,但我无法找到与基于Python的Web框架Django中的urlize
过滤器一样好的函数。因此,我将Django的urlize
函数移植到JavaScript:
一个例子:
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. <grin>"
第二个参数,如果为true,则会导致插入rel="nofollow"
。第三个参数,如果为true,则转义在HTML中具有特殊含义的字符。请参阅the README file。
答案 6 :(得分:10)
我将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>');
};
}
答案 7 :(得分:7)
答案 8 :(得分:6)
我在google上搜索了更新的内容并遇到了这个问题:
$('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/
非常适合普通链接。
答案 9 :(得分:5)
此解决方案与许多其他解决方案一样,实际上使用与其中一个相同的正则表达式,但是它不会返回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支持。
here是一个演示。
答案 10 :(得分:4)
如果您需要显示较短的链接(仅限域名),但使用相同的长网址,您可以尝试修改上面发布的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>");
}
答案 11 :(得分:3)
应该注意有关URI复杂性的警告,但对您的问题的简单回答是:
要替换所有匹配,您需要将/g
标志添加到RegEx的末尾:
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi
答案 12 :(得分:2)
Reg Ex:
/(\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>");
}
以下是一些经过测试的字符串:
注意:如果您不想将www
作为有效的传递,请使用以下reg ex:
/(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig
答案 13 :(得分:2)
保持简单!说出你不能拥有的东西,而不是你拥有的东西:)
如上所述,URL可能非常复杂,特别是在'?'之后,并非所有URL都以'www'开头。例如maps.bing.com/something?key=!"£$%^*()&lat=65&lon&lon=20
所以,不是没有一个复杂的正则表达式,不能满足所有边缘情况,而且难以维护,这个更简单的正则表达式如何在实践中对我有用。
匹配
http(s):// (anything but a space)+
www. (anything but a space)+
'任何'是[^'"<>\s]
...基本上是一个贪婪的比赛,继续你会遇到一个空格,引号,尖括号或行尾
此外:
请务必检查它是否已经采用网址格式,例如该文字包含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;
答案 14 :(得分:2)
使用国际域名进行正确的网址检测&amp;星界人物的支持并非琐碎的事情。 linkify-it
库从Mesosphere构建正则表达式,最终大小约为6千字节:)。它比所有的lib更准确,目前在接受的答案中引用。
请参阅many conditions查看所有边缘情况并测试您的情况。
如果您需要链接HTML源代码,则应首先解析它,并分别迭代每个文本标记。
答案 15 :(得分:1)
我已经写了另一个JavaScript库,它可能对你更好,因为它对最小可能的误报非常敏感,快速且小。我目前正在积极维护它,所以请测试它in the demo page,看看它对你有用。
答案 16 :(得分:0)
为我工作:
dataLayer.push({
'event': 'view_item_list',
'ecommerce': {
'items': [
{
'item_name': 'Triblend Android T-Shirt', // Name or ID is required.
'item_id': '12345',
'price': '15.25',
'item_brand': 'Google',
'item_category': 'Apparel',
'item_category2': 'Mens',
'item_category3': 'Shirts',
'item_category4': 'Tshirts',
'item_variant': 'Gray',
'item_list_name': 'Search Results',
'item_list_id': 'SR123',
'index': 1,
'quantity': '1'
},
{
'item_name': 'Donut Friday Scented T-Shirt',
'item_id': '67890',
'price': '33.75',
'item_brand': 'Google',
'item_category': 'Apparel',
'item_category2': 'Mens',
'item_category3': 'Shirts',
'item_category4': 'Tshirts',
'item_variant': 'Black',
'item_list_name': 'Search Results',
'item_list_id': 'SR123',
'index': 2,
'quantity': '1'
}]
}
});
答案 17 :(得分:0)
尝试以下解决方案
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;
}
答案 18 :(得分:0)
尝试以下功能:
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/"));
答案 19 :(得分:0)
/**
* 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/>(");
}
答案 20 :(得分:0)
这是我的解决方法:
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;
}
}
答案 21 :(得分:0)
使用HTML链接替换文本中的网址,忽略href / pre标记内的网址。 https://github.com/JimLiu/auto-link
答案 22 :(得分:0)
从多个来源输入后,我现在找到了一个效果很好的解决方案。它与编写自己的替换代码有关。
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;
});
}
答案 23 :(得分:0)
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 ”等电子邮件地址。
答案 24 :(得分:0)
我必须做相反的事情,并将html链接改为URL,但我修改了你的正则表达式,它就像一个魅力,谢谢:)
var exp = /<a\s.*href=['"](\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])['"].*>.*<\/a>/ig; source = source.replace(exp,"$1");