JS正则表达式

后盾人的文档

JS中的正则表达式

基础知识

正则表达式是用于匹配字符串中字符组合的模式,在 JavaScript中,正则表达式也是对象。

  • 正则表达式是在宿主环境下运行的,如js/php/node.js

  • 本章讲解的知识在其他语言中知识也是可用的,会有些函数使用上的区别

对比分析

与普通函数操作字符串来比较,正则表达式可以写出更简洁、功能强大的代码。

下面使用获取字符串中的所有数字来比较函数与正则的差异。

1
2
3
4
5
6
7
//过滤字符串中的数字
let hd = 'zhangjjiiallllliiiiinnnnn132';
// console.log([...hd]);//展开成一个数组。
// console.log(isNaN(hd));
let nums = [...hd].filter(a=>!Number.isNaN(parseInt(a)));
console.log(nums);
console.log(nums.join(''));//连接数字
1
2
//正则方法
console.log(hd.match(/\d/g).join(''));

创建正则

JS提供字面量与对象两种方式创建正则表达式

字面量创建

使用//包裹的字面量创建方式是推荐的作法,但它不能在其中使用变量

1
2
let mk = "miketaylor.comuuuuuu";
console.log(/u/.test(mk));//true

下面尝试使用 a 变量时将不可以查询

1
2
3
4
5
6
7
8
9
10
11
<script>
// 操作变量进行正则匹配
let mk = "miketaylorjuly123.cn";
let site= "t";
let a = "t";
console.log(/t/.test(mk)); // true
console.log(/site/.test(mk)); // false
console.log(/a/.test(mk)); //false , 测试的是a,不是t

console.log(eval(`/${a}/`).test(mk));//eval()可以把字符串变成JS表达式
</script>

对象创建

当正则需要动态创建时使用对象方式。

1
2
3
4
5
6
7
8
//对象创建正则表达式
let mk = 'miketaylorjuly123.cn';
let test1 = 'u';
let reg1 = new RegExp('u','g');
let reg2 = new RegExp(test1 ,'g');

console.log(reg1.test(mk));// true
console.log(reg2.test(mk));// true ,可以识别变量

字符串替换:

1
2
3
4
5
6
7
8
9
<script>
//字符串替换
console.log("abcd".replace('ab','@@'));
//把字符串后脏的下字母替换成@@
console.log("_a_b_c_d_".replace(/\w/g,'@@'));
console.log("_a_b_c_d_".replace(/\w/g,search => {
return '@@';
}));
</script>

根据用户输入高亮显示内容,支持用户输入正则表达式:

1
2
3
4
5
6
7
8
9
10
<script>
const mk = prompt('请输入要检测的内容,支持则正则');
const reg = new RegExp(mk, "g");
let body = document
.querySelector('#content')
.innerHTML.replace(reg, search => {
return `<span style="color:#ff0000">${search}</span>`;
});
document.body.innerHTML = body;
</script>

通过对象创建正则提取标签

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<h1>miketaylor.com</h1>
<h1>miketaylorjuly123.cn</h1>
</body>

<script>
function element(tag) {
const html = document.body.innerHTML;
let reg = new RegExp("<(" + tag + ")>.+</\\1>", "g");
return html.match(reg);
}
console.table(element("h1"));
</script>

| 这个符号带表选择修释符,也就是 | 左右两侧有一个匹配到就可以。

检测电话是否是上海或北京的坐机

1
2
3
4
5
6
let tel = "010-12345678";
//错误结果:只匹配 | 左右两边任一结果
console.log(tel.match(/010|020\-\d{7,8}/));

//正确结果:所以需要放在原子组中使用
console.log(tel.match(/(010|020)\-\d{7,8}/));

匹配字符是否包含miketaylor.cnmiketayulorjuly123.cn

字符转义

转义用于改变字符的含义,用来对某个字符有多种语义时的处理。

假如有这样的场景,如果我们想通过正则查找/符号,但是 /在正则中有特殊的意义。如果写成///这会造成解析错误,所以要使用转义语法 /\//来匹配。

字符边界

使用字符边界符用于控制匹配内容的开始与结束约定。

^ 匹配字符串的开始
$ 匹配字符串的结束,忽略换行符

检测用户名长度为3~6位,且只能为字母。如果不使用 ^与$ 限制将得不到正确结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<input type="text" name="user" />
<span></span>
</body>
<script>
let mk = 'miketaylorjuly123.cn';
//匹配字符串包含数字与否
console.log(/\d/.test(mk));
//匹配字符串开头是数字与否
console.log(/^\d/.test(mk));
//匹配字符串结尾是数字与否
console.log(/\d$/.test(mk));
//匹配字符串是否全由数字组成
console.log(/\d$/.test(mk));

// 限定起始结束匹配
document.querySelector("[name = 'user']").addEventListener("keyup",function (){
let flag = this.value.match(/^[a-z]{3,6}$/)//限定匹配3-6位的小写英文字母。
document.querySelector("span").innerHTML = flag?"正确":"错误";
});
</script>

元字符

元字符是正则表达式中的最小元素,只代表单一(一个)字符

字符列表

元字符 说明 示例
\d 匹配任意一个数字 [0-9]
\D 除了数字以外的任何一个字符匹配 [^0-9]
\w 与任意一个英文字母,数字或下划线匹配 [a-zA-Z_]
\W 除了字母,数字或下划线外与任何字符匹配 [^a-zA-Z_]
\s 任意一个空白字符匹配,如空格,制表符\t,换行符\n [\n\f\r\t\v]
\S 除了空白符外任意一个字符匹配 [^\n\f\r\t\v]
. 匹配除换行符外的任意字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
mk = "miketaylor 2021";
// console.log(mk.match(/\d\d\d\d/));
console.log(mk.match(/\d+/g)); // 多个数字匹配

//匹配电话号
// \d匹配数字
let info = `张三:010-20012932233,李四:020-2020321355`;
console.log(info.match(/\d{3}-\d+/g)); //g参数,一直扫描到结尾,返回0个或多个结果
console.log(info.match(/[-\d:,]/g)); // 匹配[]中的所有元素并输出
console.log(info.match(/[-\d:,]+/g)); // 连续匹配[]中的所有元素并输出
console.log(info.match(/[^-\d:,]/g)); // 匹配除了[]中的所有元素并输出
console.log(info.match(/[^-\d:,]+/g)); // 连续匹配除了[]中的所有元素并输出

// \D与除了数字以外的任何一个字符匹配
let mk2 = "miketaylor 2021";
console.log(mk2.match(/\D+/g)); //g参数,一直扫描到结尾,返回0个或多个结果

// \s 任意一个空白字符匹配,如空格,制表符`\t`,换行符`\n`
console.log(/\s/.test(mk2));

// \S 除了空白符外任意一个字符匹配
console.log(mk2.match(/\S+/g));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
let mk = "miketaylorjuly123.cn";
// \w与任意一个英文字母,数字或下划线匹配
console.log(mk.match(/\w+/g));

// 邮箱匹配
let email = "3105085561@qq.com";
console.log(email.match(/^\w+@\w+\.\w+$/g));

// \W除了字母,数字或下划线外与任何字符匹配
console.log('jiajialingongzhang@gmail.com'.match(/\W/g));

// 用户名匹配:5-10位,a-z开头5
let username = prompt("Enter your username");
console.log(/^[a-z]\w{4,9}$/.test(username));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
//.匹配除换行符外的任意字符
let mk = "miketaylorjuly123.cn-+!@#$@$#%%^%$&^";
console.log(mk.match(/.+/g));

// 网址匹配
let my_site = "https://miketaylorjuly123.cn";
console.log(my_site.match(/https?:\/\/\w+\.\w+/g));

let my_site2 = `
https://miketaylorjuly123.cn
https://www.baidu.com
`;

console.log(my_site2.match(/.+/s)[0]);// s模式:把匹配对象视为单行/把换行符当做普通空白使用。

// 空格处理
let tel = '155 8322 3946';
console.log(tel.match(/\d+ \d+ \d+/));
// 或console.log(tel.match(/\d+\s\d+\s\d+/));
</script>

匹配任何字符

1
2
3
4
5
<script>
// 匹配所有字符[\s\S] [\d\D] [\w\W] [.\s]
let mk = "mik1123%$#@!#%%^&%^&*()|<>,./;'[]|*/~`etaylorjuly123.cn";
console.log(/[\s\S]+/.test(mk));
</script>

模式修饰

正则表达式在执行时会按他们的默认执行方式进行,但有时候默认的处理方式总不能满足我们的需求,所以可以使用模式修正符更改默认方式。

修饰符 说明
i 不区分大小写字母的匹配
g 全局搜索所有匹配内容
m 视为多行
s 视为单行忽略换行符,使用. 可以匹配所有字符
y regexp.lastIndex 开始匹配
u 正确处理四个字符的 UTF-16 编码

模式g与模式i

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
//模式i
let mk = "miketaylorjuly123.cnV";
console.log(mk.match(/v/));
console.log(mk.match(/v/i));

//模式g
let mk2 = "miketVaVyVloVrjVuly1V23.VcVn";
console.log(mk2.match(/v/gi)); // 混合使用。

// 利用模式匹配实现替换
console.log(mk2.replace(/v/gi , '@'));
</script>

m多行匹配修正符实例

将以 #数字开始的课程解析为对象结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
let mk = `
#1 Data Structure,90 #
#2 Computer Network,90 #
#3 Operation system,85 # 张云
#4 Artificial Vision,100 #
`;
//希望提取结构[{course_name:'',scores:'',}],需要过滤掉”# 张云“项
let lessons = mk.match(/\s*#\d\s+.+#$/gm).map(v=>{
v = v.replace(/\s*#\d+\s+/,'').replace(/\s#/,'');
[name,price] = v.split(',');
return {name ,price};
})
console.log(JSON.stringify(lessons,null,2));
</script>

汉字与字符属性

每个字符都有属性,如L属性表示是字母,P 表示标点符号,需要结合 u 模式才有效。其他属性简写可以访问 属性的别名 (opens new window)网站查看。

1
2
3
let mk  = "miketaylorjuly123.加加林,冲冲冲。";
console.log(mk.match(/\p{L}/gu));// 匹配中英文字
console.log(mk.match(/\p{P}/gu)); //匹配标点符号

字符也有Unicode文字系统属性 Script=文字系统,下面是使用 \p{sc=Han} 获取中文字符 han为中文系统,其他语言请查看 文字语言表

1
2
3
4
let hd = `
张三:010-99999999,李四:020-88888888`;
let res = hd.match(/\p{sc=Han}+/gu);
console.log(res);

使用 u 模式可以正确处理四个字符的 UTF-16 字节编码

1
2
3
4
let str = "𝒳𝒴";
console.table(str.match(/[𝒳𝒴]/)); //结果为乱字符"�"

console.table(str.match(/[𝒳𝒴]/u)); //结果正确 "𝒳"

lastIndex

RegExp对象lastIndex 属性可以返回或者设置正则表达式开始匹配的位置

  • 必须结合 g 修饰符使用
  • exec 方法有效
  • 匹配完成时,lastIndex 会被重置为0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
//lastIndex
let mk = 'miketaylorjuly123.cn';
let reg = /\w/g;
console.log(mk.match(reg)); //无法保留主信息

//注意:必须/g进入全局属性
console.log(reg.lastIndex); //0
console.log(reg.exec(mk)); //m
console.log(reg.lastIndex); //1
console.log(reg.exec(mk)); //i
console.log(reg.lastIndex); //2
console.log(reg.exec(mk)); //k
console.log(reg.lastIndex); //3
console.log(reg.exec(mk)); //e
console.log(reg.lastIndex); //4
console.log(reg.exec(mk)); //t
console.log(reg.lastIndex); //9

//遍历
while((res = reg.exec(mk))){
console.log(res);
}
</script>

y

我们来对比使用 yg 模式,使用 g 模式会一直匹配字符串。

提高检索大文档的效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
// g
let mk = 'lovemikelovetaylor';
let reg = /l/g;
console.log(reg.exec(mk));
console.log(reg.lastIndex); //1
console.log(reg.exec(mk));
console.log(reg.lastIndex); //9
console.log(reg.exec(mk));
console.log(reg.lastIndex); // 16
console.log(reg.exec(mk));
console.log(reg.lastIndex); // 0
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
// g
let mk = 'lovemikelovetaylor';
let reg = /l/y;
console.log(reg.exec(mk));
console.log(reg.lastIndex); //1
console.log(reg.exec(mk));
console.log(reg.lastIndex); //0
console.log(reg.exec(mk));
console.log(reg.lastIndex); // 1
console.log(reg.exec(mk));
console.log(reg.lastIndex); // 0
</script>

因为使用 y 模式可以在匹配不到时停止匹配,在匹配下面字符中的qq时可以提高匹配效率。

1
2
3
4
5
6
7
  let hd = `铁憨憨QQ号:11111111,999999999,88888888,我将不断学习,并且称为全栈!`;

let reg = /(\d+),?/y;
let qq = [];
reg.lastIndex = 7;
while ((res = reg.exec(hd))) qq.push(res[1]);
console.log(qq);

原子表

在一组字符中匹配某个元字符,在正则表达式中通过元字符表来完成,就是放到[] (方括号)中。

使用语法

原子表 说明
[] 只匹配其中的一个原子
[^] 只匹配”除了”其中字符的任意一个原子
[0-9] 匹配0-9任何一个数字
[a-z] 匹配小写a-z任何一个字母
[A-Z] 匹配大写A-Z任何一个字母

实例操作

使用[]匹配其中任意字符即成功,下例中匹配ue任何一个字符,而不会当成一个整体来对待。

1
2
3
4
5
<script>
let mk = "miketaylorjuly123.cn";
console.log(/kt/.test(mk)); //f
console.log(/[kt]/.test(mk)); //t
</script>

日期匹配

1
2
3
4
5
<script>
// 日期的匹配
let date = "2022/02/23";
console.log(date.match(/^\d{4}([-\/])\d{2}\1\d{2}$/)); // 这里使用了原子组,取组用\1,目的是防止前/后-这类事故。
</script>

区间匹配

0-3匹配

1
2
const num = "2";
console.log(/[0-3]/.test(num)); //true

匹配a~f间的任意字符

1
2
const hd = "e";
console.log(/[a-f]/.test(hd)); //true

顺序为升序否则将报错

1
2
const num = "2";
console.log(/[3-0]/.test(num)); //SyntaxError

字母也要升序否则也报错

1
2
const hd = "houdunren.com";
console.log(/[f-a]/.test(hd)); //SyntaxError
1
2
const hd = "houdunren.com";
console.log(/[f-a]/.test(hd)); //SyntaxError

验证用户名,要求数字开头,后面数字下划线字母,4-7位

1
2
3
4
5
6
7
8
9
<body>
<input type="text" name="username">
</body>
<script>
let input = document.querySelector(`[name="username"]`);
input.addEventListener('keyup',function(){
console.log(this.value.match(/^[a-z]\w+{3,6}$/i)); // i不区分大小写 , 4- 7位
});
</script>

排除匹配

1
2
3
4
let hd = `
张三:010-99999999,李四:020-88888888`;
let res = hd.match(/[^:\d-,]+/g); // 放在第一个位置不用转义,而-不在第一个,要转义
console.log(res);

原子表中有些正则字符不需要转义,如果转义也是没问题的,可以理解为在原子表中. 就是小数点。

1
2
3
4
5
6
7
<script>
let str = "(miketaylorjuly123.cn)+";
console.table(str.match(/[().+]/g));

//使用转义也没有问题
console.table(str.match(/[\(\)\.\+]/g));
</script>

下面是使用原子表知识删除所有标题

1
2
3
4
5
6
7
8
9
10
11
<body>
<p>憨憨</p>
<h1>miketaylorjuly123.cn</h1>
<h2>baidu.fanyi.com</h2>
</body>
<script>
const body = document.body;
const reg = /<(h[1-6])>[\s\S]*<\/\1>*/gi;
let content = body.innerHTML.replace(reg, "");
document.body.innerHTML = content;
</script>

匹配所有内容

1
2
3
4
5
6
7
8
<script>
let mk = `
miketaylor
Juile
`;
console.log(mk.match(/.+/gs)); // s模式忽略换行符
console.log(mk.match(/[\S\s]+/gs)); // 匹配所有内容
</script>

原子组

  • 如果一次要匹配多个元子,可以通过元子组完成
  • 原子组与原子表的差别在于原子组一次匹配多个元子,而原子表则是匹配任意一个字符
  • 元字符组用 () 包裹

下面使用原子组匹配 h 标签。

1
2
3
4
5
6
7
8
9
<script>
let mk = `
<h1>miketaylorjuly</h1>
<h2>Linux</h2>
`;

let reg = /<(h[1-6])>[\s\S]*<\/\1>/i; //原子组 \1 \2 \3取出原子组
console.log(mk.match(reg));
</script>

基本使用

没有添加 g 模式修正符时只匹配到第一个,匹配到的信息包含以下数据

变量 说明
0 匹配到的完整内容
1,2…. 匹配到的原子组
index 原字符串中的位置
input 原字符串
groups 命名分组

match中使用原子组匹配,会将每个组数据返回到结果中

  • 0 为匹配到的完成内容
  • 1/2 等 为原子级内容
  • index 匹配的开始位置
  • input 原始数据
  • groups 组别名

邮箱匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<input type="text" name="mail" value="23134565556@qq.com">
<input type="text" name="website" value="miketaylorjuly123@qq.com.cn">
</body>
<script>
let mail = document.querySelector(`[name = "mail"]`).value;
let website = document.querySelector(`[name = "website"]`).value;
let reg = /^[\w-]+@[\w-]+\.(com|org|cc|cn|net)$/i; // 必须用原子组,不然单纯的com或cc也能直接匹配
console.log(mail.match(reg));


let reg_pro = /^[\w-]+@([\w-]+\.)+(com|org|cc|cn|net)$/i;//改良
console.log(website.match(reg_pro));
</script>

邮箱匹配并输出结果

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<input type="text" name="website" value="miketaylorjuly123@qq.com.cn">
<span></span>
</body>
<script>

let website = document.querySelector(`[name = "website"]`).addEventListener("keyup",function(){
let reg_pro = /^[\w-]+@([\w-]+\.)+(com|org|cc|cn|net)$/i;
document.querySelector("span").innerHTML = reg_pro.test(this.value)?"正确":"错误";
});

</script>

引用分组

\n 在匹配时引用原子组, $n 指在替换时使用匹配的组数据。下面将标签替换为p标签。

1
2
3
4
5
6
7
8
9
<script>
let mk = `
<h1>miketaylorjuly</h1>
<span></span>
<h2>hulu</h2>
`;
let reg = /<(h[1-6])>([\s\S]+)<\/\1>/gi;
console.log(mk.replace(reg, '<p>$2</p>'));
</script>

替换进阶

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
let mk = `
<h1>miketaylorjuly</h1>
<span></span>
<h2>hulu</h2>
`;
let reg = /<(h[1-6])>(\w([\s\S]+))<\/\1>/gi;
// console.log(mk.replace(reg, '<p>$2</p>'));
let res = mk.replace(reg, (p0,p1,p2,p3)=>{
return `<span>${p2}</span>`;
}); //p0~p3代表不同的括号内容。
console.log(res);
</script>

嵌套分组与不记录组

如果只希望组参与匹配,便不希望返回到结果中使用 (?: 处理。下面是获取所有域名的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
let mk = `
https://www.baidu.com
https://miketaylorjuly123.cn
https://www.google.com
`;
let reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|org|cn|cc))/ig; //?:不记录一块数据,也就无法使用\1或$1这种形式。
console.dir(reg.exec(mk));
console.log(reg.lastIndex); //26
console.dir(reg.exec(mk));
console.log(reg.lastIndex); // 59
console.dir(reg.exec(mk));
console.log(reg.lastIndex); //86
console.dir(reg.exec(mk));
console.log(reg.lastIndex); //0

//使用循环操作
let urls = [];
while((res = reg.exec(mk))){
urls.push(res[1]);
}
console.table(urls);
</script>

分组别名

如果希望返回的组数据更清晰,可以为原子组编号,结果将保存在返回的 groups字段中

1
2
let hd = "<h1>miketaylorjuly123.cn</h1>";
console.dir(hd.match(/<(?<tag>h[1-6])[\s\S]*<\/\1>/));

组别名使用 ?<> 形式定义,下面将标签替换为p标签

1
2
3
4
5
6
7
let hd = `
<h1>miketaylor</h1>
<span>铁憨憨</span>
<h2>macos</h2>
`;
let reg = /<(?<tag>h[1-6])>(?<con>[\s\S]*)<\/\1>/gi;
console.log(hd.replace(reg, `<p>$<con></p>`));

重复匹配

[#](https://houdunren.gitee.io/note/js/14 正则表达式.html#基本使用-2)基本使用

如果要重复匹配一些内容时我们要使用重复匹配修饰符,包括以下几种。

符号 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

因为正则最小单位是元字符,而我们很少只匹配一个元字符如a、b所以基本上重复匹配在每条正则语句中都是必用到的内容。

多重重复匹配

1
2
3
4
5
6
7
8
9
<script>
let mk= 'mkkkkkkkkkk';
console.log(mk.match(/mk+/)); // + 只影响k,不影响m,贪婪匹配模式。
console.log(mk.match(/mk*/)); // * 表示 0 个或多个
console.log(mk.match(/mk{2,5}/)); // {}为匹配个数限制
console.log(mk.match(/mk{1,}/));// {1,}等价于+
console.log(mk.match(/mk{0,}/));//{0,}等价于*
console.log(mk.match(/mk?/)); //?:0个或者1个
</script>

电话匹配正则

1
2
3
4
5
6
<script>
let mk = 'mkkkkkkkkmkmkmk';
console.log(mk.match(/(mk)+/g)); // 对原子组后面的+,影响的是整个原子组
let tel = '0315-3693243';
console.log(tel.match(/^0\d{2,3}\-\d{7,8}$/));
</script>

网站用户名验证

1
2
3
4
5
6
7
8
9
10
11
<body>
<input type="text " name="username">
</body>
<script>
document.querySelector(`[name = "username"]`).addEventListener('keyup',(e)=>{
const value =e.target.value;
console.log(value);
let reg = /^[a-z][\w-]{2,7}$/i;
console.log(reg.test(value));
});
</script>

​ 批量使用正则完成密码验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
const input = document.querySelector(`[name="password"]`);
input.addEventListener('keyup',e=>{
const value = e.target.value;
//匹配多个正则
const regs = [
/^[a-z0-9]{5,10}$/i,
/[A-Z]/,
/[0-9]/,
]
let state = regs.every(e=>e.test(value));
console.log(state?"正确":"错误");
});
</script>

禁止贪婪

正则表达式在进行重复匹配时,默认是贪婪匹配模式,也就是说会尽量匹配更多内容,但是有的时候我们并不希望他匹配更多内容,这时可以通过?进行修饰来禁止重复匹配。

使用 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
1
2
3
4
5
6
7
8
9
<script>
let mk = 'mkkkkkkk';
console.log(mk.match(/mk+?/));
console.log(mk.match(/mk*?/));
console.log(mk.match(/mk{2,100}/));
console.log(mk.match(/mk{2,100}?/));
console.log(mk.match(/mk{2,}?/));
console.log(mk.match(/mk??/));//相当于{0,1}?,禁止问号贪婪。
</script>

将所有span更换为h4 并描色,并在内容前加上 铁憨憨-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<main>
<span>wangbuer</span>
<span>baidu.com</span>
<span>miketaylorjuly123.cn</span>
</main>
</body>
<script>
//不禁止贪婪是不行的!
const main = document.querySelector("main");
const reg = /<span>([\s\S]+?)<\/span>/gi;
main.innerHTML = main.innerHTML.replace(reg,(v , p1)=>{
console.log(p1);
return `<h4 style="color:#8080ff">铁憨憨--${p1}</h4>`;
});
</script>
</html>

下面是使用禁止贪婪查找页面中的标题元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<h1>
houdunren.com
</h1>
<h2>miketaylorjuly123.cn</h2>
<h3></H3>
<H1></H1>
</body>

<script>
let body = document.body.innerHTML;
let reg = /<(h[1-6])>[\s\S]*?<\/\1>/gi;
console.table(body.match(reg));
</script>

全局匹配

问题分析

使用match 全局获取页面中标签内容,但并不会返回匹配细节。

matchAll

在新浏览器中支持使用 matchAll 操作,并返回迭代对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<h1>wangbuer</h1>
<h2>baidu.com</h2>
<h1>铁憨憨</h1>
</body>
<script>
let reg = /<(h[1-6])>([\s\S]+?)<\/\1>/ig;
const body = document.body;
console.log(body.innerHTML.match(reg)); //不能剔除标签,取不到内容,去掉g后取得到,但又不是全局了。
//最新版浏览器的方法:matchAll
const mk = body.innerHTML.matchAll(reg);
let content = [];
console.log(mk);
for(const iterator of mk){
console.dir(iterator);
content.push(iterator[2]);
}
console.table(content);
</script>

为低版本浏览器定义原型方法matchAll()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<h1>wangbuer</h1>
<h2>baidu.com</h2>
<h1>铁憨憨</h1>
</body>
<script>
//matchAll
String.prototype.matchAll=function(reg){
let res = this.match(reg);
if(res){
let str = this.replace(res[0],"^".repeat(res[0].length));
let match = str.matchAll(reg) || [];
return [res,...match];
}
};
let body = document.querySelector(`body`).innerHTML;
let search = body.matchAll(/<(h[1-6])>([\s\S]+?)<\/\1>/);
console.log(search)
</script>

exec

使用 g 模式修正符并结合 exec 循环操作可以获取结果和匹配细节

一定要用模式g!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<h1>wangbuer</h1>
<h2>baidu.com</h2>
<h1>铁憨憨</h1>
</body>
<script>
function search(string, reg) {
const matchs = [];
while ((data = reg.exec( string))) {
matchs.push(data);
}
return matchs;
}
console.log(search(document.body.innerHTML, /<(h[1-6])>[\s\S]+?<\/\1>/gi));
</script>

字符方法

下面介绍的方法是 String 提供的支持正则表达式的方法

search() 方法用于检索字符串中指定的子字符串,也可以使用正则表达式搜索,返回值为索引位置

match

直接使用字符串搜索。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
let mk = "miketaylorjuly123.cn";
console.log(mk.search(".cn")); //17
console.log(mk.search(".@")); //-1

let mk2 = `
https://miketaylorjuly123.cn
https://www.sina.com.cn
https://www.baidu.com
`;

let reg = /https?:\/\/(\w+\.)?(\w+\.)+(com|cn|org|cc)/gi;
console.log(mk2.match(reg));
</script>

matchAll

在新浏览器中支持使用 matchAll 操作,并返回迭代对象,返回由细节!

split

用于使用字符串或正则表达式分隔字符串,下面是使用字符串分隔日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
let mk2 = `
https://miketaylorjuly123.cn
https://www.sina.com.cn
https://www.baidu.com
`;

let reg = /https?:\/\/(\w+\.)?(\w+\.)+(com|cn|org|cc)/gi;
console.table(mk2.match(reg));
console.log(mk2.matchAll(reg));//返回迭代器
for (const iterator of mk2.matchAll(reg)) {
console.table(iterator);
};

//split按照给出的模板匹配。
let mk = "2021/05/29";
console.log(mk.split(/[\/-]/));
</script>

replace

replace 方法不仅可以执行基本字符替换,也可以进行正则替换,下面替换日期连接符

1
2
let str = "2023/02/12";
console.log(str.replace(/\//g, "-")); //2023-02-12
1
2
3
4
5
6
<script>
let mk = '(010)65313213 (020)3468621';
let reg = /\((\d{3,4})\)(\d{7,8})/g;
// console.log(mk.match(reg));
console.log(mk.replace(reg, '$1-$2'));
</script>

替换字符串可以插入下面的特殊变量名:

变量 说明
$$ 插入一个 “$”。
$& 插入匹配的子串。
$` 插入当前匹配的子串左边的内容。
$' 插入当前匹配的子串右边的内容。
$n 假如第一个参数是 RegExp 对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始
1
2
3
4
5
6
7
8
9
10
11
12
<script>
// let mk = '(010)65313213 (020)3468621';
// let reg = /\((\d{3,4})\)(\d{7,8})/g;
// // console.log(mk.match(reg));
// console.log(mk.replace(reg, '$1-$2'));

//$` $' $&
let mk = '=%铁憨憨@=';
console.log(mk.replace(/铁憨憨/,"==$&==")); //$&表示匹配到的内容
console.log(mk.replace(/铁憨憨/,"$`")); //把匹配到的内容替换为匹配到的内容的前面部分
console.log(mk.replace(/铁憨憨/,"$'")); //把匹配到的内容替换为匹配到的内容的后面部分
</script>

把所有准则汉字加上链接 百度百科链接。

1
2
3
4
5
6
7
8
9
<body>
<main>
核心价值观,简单来说就是某一社会群体判断社会事务时依据的是非标准,遵循的行为准则。
</main>
</body>
<script>
const main = document.querySelector("body main");
main.innerHTML = main.innerHTML.replace(/准则/g,`<a href="https://baike.baidu.com/item%E6%A0%B8%E5%BF%83%E4%BB%B7%E5%80%BC%E8%A7%82/6946423?fr=aladdin">$&</a>`);
</script>

为链接添加上https ,并补全 www.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<main>
<a style="color:red" href="http://www.hdcms.com">
开源系统
</a>
<a id="l1" href="http://houdunren.com">后盾人</a>
<a href="http://yahoo.com">雅虎</a>
<h4>http://www.hdcms.com</h4>
</main>
</body>
<script>
const main = document.querySelector("body main");
const reg = /(<a.*href=['"])(http)(:\/\/)(www\.)?(hdcms|houdunren)/gi;
main.innerHTML = main.innerHTML.replace(reg, (v, ...args) => {
args[1] += "s";
args[3] = args[3] || "www.";
return args.splice(0, 5).join("");
});
</script>

把h标签全换成h4。

1
2
3
4
5
6
7
8
9
<script>
let mk = `
<h1>miketaylor</h1>
<span>铁憨憨</span>
<h2>baidu</h2>
`;
const reg = /<(h[1-6])>(?<con>.*?)<\/\1>/g;
console.log(mk.replace(reg,'<h4><con><h4>'));
</script>

使用原子组命名优化正则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<main>
<a href="miketaylorjuly123.cn">个人站点</a>
<a href="www.baidu.com">百度</a>
<a href="www.google.com">谷歌</a>
</main>
</body>
<script>
// [{link:'',title:''}]提取结果
const main = document.querySelector("body main");
const reg = /<a.*?href=(["'])(?<link>.*?)\1>(?<title>.*?)<\/a>/gi;
// console.log(main.innerHTML.match(reg));
const links = [];
for (const iterator of main.innerHTML.matchAll(reg)){
links.push(iterator["groups"]);
}
console.log(links);
</script>

test方法

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<input type="text" name="email">
</body>
<script>
const mail = document.querySelector(`[name="email"]`);
mail.addEventListener("keyup",e=>{
let value = e.target.value;
//允许aaaaaaa@sina.com.cn这种结构
let flag = /^[\w-]+@(\w+\.)+(com|cn|cc|org)$/i.test(value);
console.log(flag);
});
</script>

回调函数

replace 支持回调函数操作,用于处理复杂的替换逻辑

变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, ... 假如replace()方法的第一个参数是一个 RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+p2 就是匹配的 \b+
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1)
string 被匹配的原字符串。
NamedCaptureGroup 命名捕获组匹配的对象

使用回调函数将 后盾人 添加上链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div class="content">
后盾人不断更新优质视频教程
</div>
</body>

<script>
let content = document.querySelector(".content");
content.innerHTML = content.innerHTML.replace("后盾人", function(
search,
pos,
source
) {
return `<a href="https://www.houdunren.com">${search}</a>`;
});
</script>

为所有标题添加上 hot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body>
<div class="content">
<h1>后盾人</h1>
<h2>houdunren.com</h2>
<h1>后盾人</h1>
</div>
</body>
<script>
let content = document.querySelector(".content");
let reg = /<(h[1-6])>([\s\S]*?)<\/\1>/gi;
content.innerHTML = content.innerHTML.replace(
reg,
(
search, //匹配到的字符
p1, //第一个原子组
p2, //第二个原子组
index, //索引位置
source //原字符
) => {
return `
<${p1} class="hot">${p2}</${p1}>
`;
}
);
</script>

断言匹配

断言虽然写在扩号中但它不是组,所以不会在匹配结果中保存,可以将断言理解为正则中的条件。

(?=exp)

零宽先行断言 ?=exp 匹配后面为 exp 的内容

把后面是miketaylor加上链接

1
2
3
4
5
6
7
8
9
    <main>
miketaylor会不断学习,所以,只要坚持,miketaylor就会成为全栈!
</main>
</body>
<script>
let main = document.querySelector('body main');
let reg = /miketaylor(?=就)/g; //?=后面为条件
main.innerHTML = main.innerHTML.replace(reg,`<a href="http://miketaylorjuly123.cn">$&</a>`);
</script>

使用断言规范分数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
let mk = `
Data Structure,90分
Computer Network,90分
Operation system,85.0分
Artificial Vision,100分
`;
let reg = /(\d+)(.0)?(?=分)/gi;
let mk2 = mk.replace(reg, (v, ... args)=>{
console.log(args);
args[1] = args[1] || '.0';
return args.splice(0,2).join('');
});
console.log(mk2)
</script>

使用断言验证用户名必须为五位,下面正则体现断言是不是组,并且不在匹配结果中记录。

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<input type="text" name="username" />
</body>

<script>
document
.querySelector(`[name="username"]`)
.addEventListener("keyup", function() {
let reg = /^(?=[a-z]{5}$)/i;
console.log(reg.test(this.value));
});
</script>

(?<=exp)

零宽后行断言 ?<=exp 匹配前面为 exp 的内容

匹配miketaylor后面的数字

1
2
3
4
5
<script>
let mk = "miketaylorjuly123.cn";
let reg = /(?<=miketaylorjuly)\d+/i;
console.log(mk.match(reg)); //123
</script>

所有超链接替换为https://miketaylorjuly123.cn

1
2
3
4
5
6
7
<script>
//网址替换
const main = document.querySelector("main");
let reg = /(?<=href=(['"])).+(?=\1)/ig;
// console.log(main.innerHTML.match(reg));
main.innerHTML = main.innerHTML.replace(reg,"https://miketaylorjuly123.cn");
</script>

使用断言模糊电话号码

1
2
3
4
5
6
7
8
9
10
11
12
<script>
let users = `
我的电话:15583703266
备用电话:15581664469
`;

let reg = /(?<=\d{7})\d{4}/gi;
users = users.replace(reg , (v) =>{
return '*'.repeat(4);
});
console.log(users);
</script>

获取标题中的内容

1
2
3
let hd = `<h1>我真的是铁憨憨</h1>`;
let reg = /(?<=<h1>).*(?=<\/h1>)/g;
console.log(hd.match(reg));

(?!exp)

零宽负向先行断言 后面不能出现 exp 指定的内容

使用 (?!exp)字母后面不能为两位数字

1
2
3
4
5
<script>
let mk = 'miketaylorjuly1232013dada';
let reg = /[a-z]+(?!\d)$/i;
console.log(mk.match(reg));
</script>

断言限制用户关键词

1
2
3
4
5
6
7
8
9
10
<body>
<input type="text" name="username">
</body>
<script>
const input = document.querySelector(`[name="username"]`);
input.addEventListener('keyup', function(){
let reg = /^(?!.*加加林.*)[a-z]{5,6}$/i;
console.log(this.value.match(reg))
})
</script>

(?<!exp)

零宽负向后行断言 前面不能出现exp指定的内容

获取前面不是数字的字符

1
2
3
4
5
6
7
<script>
let mk = 'miketaylorjuly1232013dada';
let reg = /(?<!\d+)[a-z]+/i;
let reg2 = /(?<!\d+).+/i;
console.log(mk.match(reg));
console.log(mk.match(reg2));
</script>

使用断言排除法同一数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<main>
<a href="https://www.houdunren.com/1.jpg">1.jpg</a>
<a href="https://oss.houdunren.com/2.jpg">2.jpg</a>
<a href="https://cdn.houdunren.com/2.jpg">3.jpg</a>
<a href="https://houdunren.com/2.jpg">3.jpg</a>
</main>
</body>
<script>
const main = document.querySelector("main");
const reg = /https:\/\/(\w+)?(?<!oss)\..+?(?=\/)/gi;
main.innerHTML = main.innerHTML.replace(reg, v => {
console.log(v);
return "https://oss.houdunren.com";
});
</script>

上完正则啦,感谢后端人教程UP主。