【效率】正则表达式

正则表达式在某些方面可以让你提升更高的效率

前言

关于正则相关的基础知识可以在 r2coding 网站中详细查看

正则相关方法可以在 @myzhibie 中查看

本篇文章主要积累在工作中遇到的某些问题或者需求

需求

a.匹配URL中的协议域名端口

文本:http://127.0.0.1:8080/vue/index.html

正则:/(\w+):\/\/([^/:]+)(:\d*)?/

方法:Reg.exec(String),通过执行该方法可以得到一个匹配数组,分别为:

  • 完整匹配

  • 子分组1匹配

  • 子分组2匹配

  • 子分组3匹配

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const reg = /(\w+):\/\/([^/:]+)(:\d*)?/;
const url = 'http://127.0.0.1:8080/vue/index.html';
const result = reg.exec(url);
console.log(result);
// 执行结果如下:
[
0: "http://127.0.0.1:8080"
1: "http"
2: "127.0.0.1"
3: ":8080"
groups: undefined
index: 0
input: "http://127.0.0.1:8080/vue/index.html"
]

分析:

  • /(\w+):\/\/([^/:]+)(:\d*)?/ 可以看到该正则有3个分组,整体可匹配到标准URL中的协议、IP、端口

  • (\w+):\/\/ 一次或多次匹配 :// 之前的数字、字母、下划线(即协议)

  • ([^/:]+) 一次或多次匹配除了 :/ 以外的内容(即IP)

  • (:\d*)? 零次或多次匹配 : 之后的数字,同时零次或一次匹配整个表达式(即端口可有可无)

b.匹配HTML标签

需求:以 <audio> 标签为例,在一个字符串中筛选出是否携带audio标签

文本:我是开头<audio id="audio-el" autoplay controls></audio>看看能不能截取audio标签

正则:/<audio(\s.+?)*>(.*?)<\/audio>/g

方法:String.match(Reg)

代码:

1
2
3
4
5
6
7
8
const reg = /<audio(\s.+?)*>(.*?)<\/audio>/g;
const content = '我是开头<audio id="audio-el" autoplay controls></audio>看看能不能截取audio标签';
const result = content.match(reg);
console.log(result);
// 执行结果如下:
[
0: "<audio id="audio-el" autoplay controls></audio>"
]

分析:

  • /<audio(\s.+?)*>(.*?)<\/audio>/g 全局匹配下执行 match() 方法会将所有匹配到的结果在一个数组中全部返回

  • <audio(\s.+?)*> 匹配标签头,标签头中可包含多种属性,因此使用 (\s.+?)* 匹配带有空格与任意字符的组零次或多次

  • (.*?) 匹配标签中的内容,除换行符之外的任意字符零次或多次

  • <\/audio> 匹配结束标签

c.替换URL中的双斜杠为单斜杠

需求:防止接口请求拼接URl时导致多出斜杠问题,需要手动将非首个的双斜杠替换为单斜杠

文本:http://127.0.0.1:8080//demo/list

正则:/([^:])(\/\/*)/g

方法:String.replace(reg, string) 将匹配结果替换为指定字符串

代码:

1
2
3
4
5
6
const reg = /([^:])(\/\/*)/g;
const url = 'http://127.0.0.1:8080//demo/list';
const result = url.replace(reg, '$1/');
console.log(result);
// 执行结果如下:
// http://127.0.0.1:8080/demo/list

分析:

  • ([^:]) 匹配区域首位不是冒号(防止协议处的双斜杠被匹配到)

  • (\/\/*) 2个或2个以上双斜杠

  • replace 方法中的 $1 代表第一个正则匹配到的内容

d.匹配字符串中的Emoji表情

需求:主要用在字符串超长截取时,因Emoji表情在显示时占两个字符,在存储时占四个字节,存在截取出错的问题

文本:😀😁

正则:太长了,看下面代码中吧

方法:Reg.test(String) 匹配该字符串中是否包含Emoji表情

代码:

1
2
3
4
5
6
7
const reg = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c[\ude32-\ude3a]|[\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/;
const arr = ['😀😁', '文本', 'abc', '123', 'a😀'];
for (let i = 0; i < arr.length; i++) {
console.log(reg.test(arr[i]));
}
// 执行结果如下:
// true false false false true

分析:

  • 该正则由 Kevin Scott 整理

  • 根据不同表情类别进行了区分

建议:因直接截取字符串会存在将Emoji表情错误拆分导致乱码,建议使用 Array.form() 将字符串转化为数组之后,再判断每项是否包含Emoji表情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const reg = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c[\ude32-\ude3a]|[\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/;
const str = '😀文本😥abc😛123😀😥';
const arr = Array.from(str);
arr.forEach((item) => {
console.log(item, '中是否包含表情:', reg.test(item));
});
// 执行结果如下:
// 😀 中是否包含表情: true
// 文 中是否包含表情: false
// 本 中是否包含表情: false
// 😥 中是否包含表情: true
// a 中是否包含表情: false
// b 中是否包含表情: false
// c 中是否包含表情: false
// 😛 中是否包含表情: true
// 1 中是否包含表情: false
// 2 中是否包含表情: false
// 3 中是否包含表情: false
// 😀 中是否包含表情: true
// 😥 中是否包含表情: true