一开始接到这个需求觉得简单,结果越搞越复杂,反复了很多次,没有特别好的解决方案
最近接到一个需求,客服输入框需要将发送出去的消息中含有url地址匹配出来添加上a标签,但是由于输入框是富文本,所以输入框内有可能有各种标签,像a、p标签等。
最开始我在网上找到一个正则表达式,但是会匹配邮箱、数字(类似www123@fenxin.com、123.123、hae.123),后来经过好几轮正则替换。
A同事找的:
(((ht|f)tps?):\/\/)?[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?(?<!@[\w\-]+(\.[\w\-]+)*)
这个不匹配邮箱,但是客户有数字123.123、密码hahh.123这种也会匹配,不确定可能得情况,进行一一排除不太好。
B同事自己写的:
^((https|http)://)?[A-Za-z0-9+&#/%?=~_|!:,.;]+\.(com|net|cn|vip|org|edu|info|gov|top|xyz|html)$
这个只能完全匹配单个url的情况,多个url不匹配、邮箱、数字、ip等不匹配,本想着就这样限制死算了,结果最近又发现富文本消息默认有p标签等,我是将p标签替换掉之后使用正则匹配的,这样本来换行的消息就不换行了,于是又要改正则了。
最终方案:
C同事写的正则(无法排除邮箱,需要加一些限制):
具体逻辑:
首先判断消息中是否有a标签,如果有则原样返回,不做处理
let aExp = /<a.+?href=\"(.+?)\".*>(.+)<\/a>/gi
let textStr = text.replace(/<p>/g,'').replace(/<\/p>/g,'').replace(/>/g,'>').replace(/</g,'<')
if(aExp.test(textStr)){
return text
}
如果没有,则判断是否含有邮箱,如果有将邮箱用特殊字符“_$1_temp”替换,然后在进行url正则匹配;最后利用匹配出来的邮箱数组,批量将特殊字符再替换回来。
let exp = /(https?:\/\/)?((((25[0-5])|(2[0-4][0-9])|(1\d{2})|(\d{2})|([1-9]))(\.((25[0-5])|(2[0-4][0-9])|(1\d{2})|(\d{2})|(\d))){2}\.((25[0-5])|(2[0-4][0-9])|(1\d{2})|(\d{2})|(\d)))|((([A-Za-z0-9][A-Za-z0-9\-]{0,19})\.)?([A-Za-z0-9][A-Za-z0-9\-]{0,19})\.([a-z]{2,3})))((:\d{2,5})?)(((\/(\S+)?)+(\.\S+)?)?)((\?(\S+\=\S+)(&(\S+\=\S+))*)?)/gi
let emailExp = /(?:[a-z0-9+!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/gi
let emailArr = text.match(emailExp)
let a2 = text.replace(emailExp, function(){
return '_$1_temp'
}).replace(exp, function(a){
if(!a.indexOf('http')){
return '<a href="'+a+'" target="_blank">'+a+'</a>'
} else {
return '<a href="http://'+a+'" target="_blank">http://'+a+'</a>'
}
})
if(emailArr&&emailArr.length>0){
let result = emailArr.map((item, i) => {
a2 = a2.replace('_$1_temp',item)
return a2
})
return result[result.length - 1]
} else {
return a2
}
目前这个方法不是太好,如果消息中存在a标签,就没有做其他url替换;正常的逻辑是a标签的url不替换,其他正常的url替换,目前这方式还不知道有没有其他问题。
不知道有没有大佬有完美的解决方案,可以满足匹配富文本消息中所有url批量替换加上a标签;同时不匹配:带有a标签的url、邮箱、123.23、haha.123、123.123.123等不是真正url的字符串,富文本消息中的标签正常返回,不影响换行等样式。