正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。
搜索模式可用于文本搜索和文本替换。
1. 什么是正则表达式?
正则表达式是由一个字符序列形成的搜索模式。
当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容。
正则表达式可以是一个简单的字符,或一个更复杂的模式。
正则表达式可用于所有文本搜索和文本替换的操作。
2. 语法
/正则表达式主体/修饰符(可选)
其中修饰符是可选的。
初探正则表达式的魅力
let str = "hello2200world6688";
// 我们要找到字符串str中的数字并拼接成新的字符串
// 不使用正则表达式的话
console.log([...str].filter(a => !Number.isNaN(parseInt(a))).join(""));
// 如果使用正则表达式可以很轻松实现
console.log(str.match(/\d/g).join(""));
3. 正则表达式的创建
-
时候用字面量
例如/\d/g
-
使用构造函数
let reg = new RegExp("\\d, "g");
4. 选择符的使用
console.log(/a|u/); // 表示监测字符串中a或者u是否存在, 也可以写成这样console.log([au]);表示匹配中括号中其中一个字符
let tel = "010-9999999";
// 下面的检测放阿飞010也为true
console.log(/010|020\-\d{7,8}/.test(tel));
//可以写成这样
console.log(/010\-\d{7,8}|020\-\d{7,8}/.test(tel));
// 更好的是使用原子组,用小括号包围的被视为一组,但是如果是abc010-9999999还是检测为true
console.log(/(010|020)\-\d{7,8}/.test(tel));
// 针对上述问题修改
console.log(/^(010|020)\-\d{7,8}$/.test("abc010-9999999")); // false
5. 字符的转义
// .表示除换行外任意字符 \.则表示普通的.
console.log(/\d+\.\d+/.test(23.34));
// 如果用构造函数的话对\d也需要转义
console.log(new RegExp("\\d+\\.\\d+").test(23.34));
// 如匹配一个网址, 左斜杠需要转义
let url = "https://www.baidu.com";
console.log(/https?:\/\/\w+\.\w+\.\w+/.test(url));
6. 字符的边界约束
使用^
表示起始, 使用$
表示结束
console.log(/^\d+$/.test("1213"));
console.log(/^[a-z]{3,6}$/.test("abc"));
7. 数值与元字符
\d
匹配数值
console.log("helloworld 2020".match(/\d+/g));
console.log("张三:010-9999999,李四:020-8888888".match(/\d{3}-\d{7,8}/g));
// 排除,下面使用^排除掉原子组中的字符,进而取出中文字符
console.log("张三:010-9999999,李四:020-8888888".match(/[^-\d:,]+/g));
\D
表示非数值
console.log("123hello".match(/\D+/g));
\s
表示空白(包括空格,换行,制表符等)
console.log(/\s/.test(" hello")); // true
\S
表示非空白符
console.log(/\S/);
[\s\S]
表示任意字符
console.log(/[\s\S]+/.test("hello"));
\w
表示字母,数字,下划线
let email = "123456789@qq.com";
console.log(email.match(/^\w+@\w+\.\w+$/));
\W
元字符用于查找非单词字符。
单词字符包括:a-z、A-Z、0-9,以及下划线。
console.log("helloworld100%".match(/\W+/g)); // ['%!']
.
点元字符表示除换行符外任意字符
console.log("hello123_\n===++".match(/.+/g));
// 使用s单行模式进行匹配,下面使用模板字符串
let str = `
hello
helloworld
`;
console.log(str.match(/.+/s)[0]);
/*
hello
helloworld
*/
// 空格是普通字符,可以直接输入也可以使用\s进行匹配
console.log("010 - 8888888").match(/\d+ - \d{8}/);
匹配所有字符
可以使用[\s\S]
或者[\d\D]
8. 模式修正符
i
不区分大小写
g
全局匹配(不使用则匹配到1项即停止)
s
单行匹配
m
把多行文本的每一行单独处理,
u
模式用来处理宽字节字符(4字节)
y
模式连续满足条件的匹配(如/\u/y
在执行匹配时候需要保证每一次继续匹配需要连续满足条件)
console.log("helloHELLO".match(/he/ig/));
下面是一个使用m
模式的实例
let str = `
#1 js,200元 #
#2 php,300元 #
#9 baidu.com # 百度
# 3 node.js,180元 #
`;
let arr = str.match(/^\s*#\d+\s+.+\s+#$/gm);
console.log(arr);
arr = arr.map(v => {
v = v.replace(/\s*#\d+\s*/).replace(/\s*#/, "");
let [name, price] = v.split(",");
return [name, price];
});
console.log(JSON.stringify(arr, null, 2));
9.字符属性和中文的匹配
字符属性
元字符 | 含义 |
---|---|
\p{L} | 所有字母 |
\p{N} | 所有数字,类似于 \d |
[\p{N}\p{L}] | 所有数字和所有字母,类似于 \w |
\P{L} | 不是字母 |
\P{N} | 不是数字 |
\p{P} | 匹配标点符号 |
console.log("hello123".match(/\p{L}+/gu)); // hello
// 匹配标点符号
console.log("hello,12!3?").match(/\p{P}+/gu); // [',','!', '?']
使用字符属性Script来根据语言系统查找对应字符
console.log(/\p{sc=Han}/gu); // 中文
10. lastIndex属性的使用
定义开始检索的位置, 需要使用g
模式,否则lastIndex属性一直为0, 需要查看位置信息等属性时候需要使用exec
来进行正则匹配
let reg = /\w/g;
while((res = reg.exec("hello world"))){
console.log(res.lastIndex);
console.log(res);
}
11.使用y模式
let str = `
QQ群:11111111, 22222222, 333333333
欢迎入群, 欢迎入群
`;
let reg = /(\d+),?/y;
reg.lastIndex = 7;
let qq = [];
while((res = reg.exec(str))) qq.push(res[1]);
console.log(qq);
12.原子组的基本使用
let date = '2020-02-23';
let reg = /^\d{4}[-\/]\d{2}[-\/]\d{2}$/;
console.log(reg.test(date););
// 可以使用原子组复用规则
let reg1 = /^\d{4}([-\/]\d{2}\1\d{2})/; // \1 即复用[-\/]
console.log(reg.test(date)); // 和上述写法等效
区间匹配
console.log(/[0-9]+/g.test("2020"));
console.log(/[a-z]+/g.test("hello"));
排除匹配
let str = "张三:010-88888888,李四:020-99999999";
console.log(str.match(/[^\d:\-,]+/g)); // ['张三', '李四']
原子组字符不解析
let str = "(hello).+";
console.log(str.match(/[(.+)]/gi)); // ( ) . +均被视为普通字符处理
邮箱验证中原子组的使用
let email = "123456789@163.com.cn";
let reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+(\.([a-zA-Z]{2,4}))+$/;
console.log(reg.test(email)); // true
使用原子组完成替换操作
let str = `
<h1>hello</h1>
<span>hello</span>
<h2>hello</h2>
`;
let reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi;
console.log(str.replace(reg, `<p>$2</p>`));
// 我们对于分组在replace第二参数使用函数时候也能够获取到分组内的匹配值
console.log(str.replace(reg, (p0,p1,p2) => `<p>${p2}</p>`);
嵌套分组与不记录分组
let str = `
https://www.baidu.com
http://google.com
https://4399.com
`;
let reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|org|cn))/gi; // 原子组内的?:表示该组不被记录分组,即不能通过$1,$2等方式获取到
13.使用正则操纵DOM元素
如删除页面中所有的h1-h6标签
let body = document.body;
let reg = /<(h[1-6])>[\s\S]*<\/\1>/;
body.innerHTML = body.innerHTML.replace(reg, "");
批量正则验证
<input type="text" name="password" id="password">
<span id="result"></span>
<script>
document.getElementById("password").addEventListener("keyup", function(){
let reg = [/[\w\-]{5,10}/, /[A-Z].{4,9}/];
if(reg.every(e => e.test(this.value))){ // every所有元素回调均返回true,结果才为true
document.getElementById("result").innerHTML = "验证成功";
} else {
document.getElementById("result").innerHTML = "验证失败";
}
});
</script>
14. 禁止贪婪
通过在+
, *
和?
后添加?
来达到禁止贪婪的效果
/str+?/
/str*?/
/str??/
标签替换中的禁止贪婪的使用
<main>
<span>hello</span>
<span>world</span>
<span>hahaha</span>
</main>
<script>
const main = document.querySelector("main");
const reg = /<span>([\s\S]*?)<\/span>/gi; // 在*后添加?禁止贪婪,要不然会匹配到main中所有内容
main.innerHTML = main.innerHTML.replace(reg, (v,p1) => {
return `<h4 style="color: red;">${p1}</h4>`;
});
</script>
15.matchAll
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Veniam dolorum minima ipsam at nemo, possimus accusantium consectetur error suscipit harum quidem consequatur repellendus reprehenderit quisquam architecto praesentium voluptate? Soluta, est?</p>
<h1>hello</h1>
<h2>world</h2>
<h3></h3>
<script>
let body = document.body;
let reg = /<(h[1-6])>([\s\S]+?)<\/\1>/gi;
let it = body.innerHTML.matchAll(reg);
let contents = [];
for(const item of it){
contents.push(item[2]);
}
console.table(contents);
</script>
为低端浏览器定义原型方法match
String.prototype.matchAll = function(){
let res = this.match(reg);
if(res){
let str = this.replace(res[0]).replace(res[0], '^'.repeat(res[0].length));
let match = str.match(reg) || [];
return [res, match];
}
};
let str = `
<p>hello</p>
<h1>world</h1>
<h2>hahaha</h2>
`;
let reg = /<(h[1-6])>([\s\S]+?)<\/\1>/i;
let it = str.matchAll(reg);
let contents = [];
for(const item of it){
contents.push(item[2]);
}
console.log(contents);
16. 字符串正则方法
方法名 | 用法 |
---|---|
test | 测试字符串是否满足正则规则,返回值为boolean |
match | 配合g模式符进行全局匹配不返回匹配细节,不加g模式客户返回第一个匹配位置的细节 |
matchALl | 全局匹配,返回一个迭代器,使用for…of循环可以获取所有匹配项的细节 |
exec | 需要配置g模式使用,否则会一直在同一位置进行匹配,使用while循环进行全局匹配时候如果忘记加g模式符会导致死循环, 每次执行exec, reg对象的lastIndex会进行更新 |
17. $`, $’, $&在字符串替换中的使用(了解)
let str = "=前端梦%";
let reg = /前端梦/;
// $&代表匹配到的内容
console.log(str.replace(reg, "$&")); // =前端梦%
console.log(str.replace(reg, "$`")); // ==%
console.log(str.replace(reg, "$'")); // =%%
**$&**的一个使用实例
<body>
<main>
前端梦,前端梦
</main>
<script>
const main = document.querySelector("body main");
main.innerHTML = main.innerHTML.replace(/前端梦/g, "<a href='www.qianduanmeng.com'>$&</a>");
</script>
</body>
18. 原子组在替换中的小技巧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<main>
<a href="http://www.baidu.com">百度</a>
<a href="http://frontender.com">前端人</a>
<a href="http://w3c.org">w3c</a>
</main>
<script>
/**
* 我们要给http后没有s的添加s,没有www的添加www
* */
const main = document.querySelector("main");
const reg = /(<a.*href=['"])(http)(:\/\/)(www\.)?/gi;
main.innerHTML = main.innerHTML.replace(reg, (...args) => {
args[2] += 's';
args[4] = args[4] || "www.";
console.log(args);
return args.slice(1,5).join("");
});
</script>
</body>
</html>
19. 原子组的别名
可以在原子组内使用?<别名>
的方式来为每个原子组定义别名
<body>
<h1>hello</h1>
<h2>world</h2>
<h3>hahaha</h3>
<script>
let body = document.body;
const reg = /<(h[1-6])>(?<con>.*?)<\/\1>/gi;
body.innerHTML = body.innerHTML.replace(reg, "<h4>$<con></h4>");
</script>
</body>
使用原子组别名来优化正则
<body>
<main>
<a href="https://www.baidu.com">百度</a>
<a href="https://www.sina.com">新浪</a>
<a href="http://frontender">前端人</a>
</main>
<script>
const main = document.querySelector("body main");
const reg = /<a.*?href=(['"])(?<link>.*?)\1>(?<title>.*?)<\/a>/gi;
const links = [];
for(const it of main.innerHTML.matchAll(reg)){
links.push(it.groups);
}
console.log(links);
</script>
</body>
20. ?=先行断言匹配(可以理解为后面是什么的)
<body>
<main>
欢迎来到前端人,为大家分享web前端开发常用技术教程
</main>
<script>
let main = document.querySelector("main");
let reg = /web前端(?=开发)/g;
main.innerHTML = main.innerHTML.replace(reg, `<a href="www.baidu.com">$&</a>`);
</script>
</body>
上面的正则表示要匹配后面是开发的web前端字样,断言匹配可以理解为是正则表达式的条件判断
使用先行断言匹配来规范价格
let lessons = `
js,200元,300次
php,300.00元,100次
node.js,180元,260次
`;
let reg = /(\d+)(.00)?(?=元)/gi;
lessons = lessons.replace(reg, (v, ...args) => {
args[1] = args[1] || '.00';
return args.splice(0,2).join("");
});
console.log(lessons);
21. ?<=后行断言(可以理解成前面是什么的)
let str = "hello123world456";
let reg = /(?<=hello)\d+/gi; // 匹配前面是hello的数字
console.log(str.match(reg));
结合使用前后行断言批量替换链接地址
<body>
<a href="https://www.baidu.com">百度</a>
<a href="https://yahoo.com">雅虎</a>
<script>
const body = document.body;
const reg = /(?<=href=(['"])).+(?=\1)/gi;
body.innerHTML = body.innerHTML.replace(reg, "https://demo.com");
</script>
</body>
使用断言模糊电话号码
let users = `
Tom: 1234567890
Jack: 9876543210
`;
let reg = /(?<=\d{6})\d{4}/gi;
users = users.replace(reg, v => {
return "*".repeat(4);
});
console.log(users);
22. ?!负向先行断言(后面不是什么的)
let str = "hello123hdms";
let reg = /[a-z]+(?!\d+)$/; // 取得后面不是数字的字母
console.log(str.match(reg));
使用断言限制用户名关键词
<body>
<input type="text" name="username" id="username">
<script>
const username = document.querySelector("[name='username']");
username.addEventListener("keyup", function(){
const reg = /^(?!.*lee.*)[a-z]{5,6}$/i;
console.log(reg.test(this.value));
});
</script>
</body>
23. ?<!负向后行断言(前面不是什么的)
下面代码匹配前面不是hello的数字
let str = "hello123world456";
let reg = /(?<!hello)\d+/gi;
console.log(str.match(reg));
24. 使用断言排除法统一数据
<body>
<main>
<a href="https://www.baidu.com/1.jpg">1.jpg</a>
<a href="https://oss.hello.com/2.jpg">2.jpg</a>
<a href="https://cdn.world.com/3.jpg">3.jpg</a>
<a href="https://haha.com/4.jpg">4.jpg</a>
</main>
<script>
const main = document.querySelector("main");
const reg = /https:\/\/([a-z]+)?(?<!oss)\..+?(?=\/)/gi; // 排除掉开头是oss的地址,把不是oss开头的地址替换为https://oss.bestcdn.com
main.innerHTML = main.innerHTML.replace(reg, v => {
return 'https://oss.bestcdn.com';
});
</script>
</body>