# 一、正则相关方法
字符串是正则表达式主要的作用对象
# 正则模式
由于在后面的 match()、exec()、replace()中需要使用全局匹配标志g
,所以先提一嘴 😂
注意,下面的内容是可以组合使用的:
/.../gi
、/.../gm
表达式 | 描述 |
---|---|
/.../i | 忽略大小写 |
/.../g | 全局匹配: 检索整字符串 |
/.../m 🤔 | 多行匹配:单独匹配每一行 |
/.../s | 单行匹配: 将多行变为单行 |
/.../u | unicode(utf-8)匹配: 将多行变为单行(主要针对多字节比如汉字) |
…… | …… |
多行匹配
let data = `
#1 js,200元 #
#2 php,300元 #
#9 ren-sir.cn # 任先生
#3 node.js,180元 #
`
let newData = data.match(/^\s*#\d+\s+.+\s+#$/gm).map((item) => {
return item.replace(/\s+#\d\s*/, '').replace(/\s+#/, '')
})
console.log(newData) // ["js,200元", "php,300元", "node.js,180元"]
let dataObj = newData.map((item) => {
;[name, price] = item.split(',')
return { name, price }
})
console.log(dataObj) // [{"name":"js","price":"200元"},{"name":"php","price":"300元"},{"name":"node.js","price":"180元"}]
unicode(utf-8)匹配
https://zh.javascript.info/regexp-unicode
- 应用:匹配中文
;/\p{sc=Han}+/gu
# 1、test()方法 🎈
对指定的字符串内容进行正则测试,返回一个布尔值。
let str = 'cats and dogs, cats and pigs'
// 直接使用正则 ✨
console.log(/cat/.test(str)) // true
// 使用正则变量 👀
let regex1 = /cat/
console.log(eval(`${regex1}`).test(str)) // true
// 正则对象 ✨
let regex2 = new RegExp('cat')
console.log(regex2.test(str)) // true
RegExp 对象
var regex = /xyz/
var regex = /xyz/i
// 等价于
var regex = new RegExp('xyz')
var regex = new RegExp('xyz', 'i')
# 2、exec()方法 🎈
exec() 方法用于在字符串中搜索匹配正则表达式的第一个位置,返回一个数组,其中存放着匹配成功的结果。
let str = 'cats and dogs, cats and pigs'
// 直接使用正则 ✨
let match = /cat/.exec(str)
console.log(match) // ["cat"]
// 使用正则变量👀
let regex1 = /cat/
let match1 = eval(`${regex1}`).exec(str)
console.log(match1) // ["cat"]
// 正则对象 ✨
let regex2 = new RegExp('cat')
let match2 = regex2.exec(str)
console.log(match2) // ["cat"]
# 3、match() 方法 ✔
字符串对象的 replace 方法可以获取匹配字符串内容的数组
- 若成功返回一个字符串数组
- 若失败返回 null
let str = 'len666camo555'
// let numbs = str.match(/\d+/g) // ["666", "555"]
let numbs = str.match(/\d+/g).join('') // "666555"
# 4、replace()方法 ✔
字符串对象的 replace 方法可以替换匹配字符串的内容
let str = 'This is Microsoft, welcome to visit Microsoft company! '
let targetValue = 'Microsoft'
// 1、使用字符串
// 替换一个
console.log(str.replace(`${targetValue}`, 'one'))
// 全局替换
console.log(str.replaceAll(`${targetValue}`, 'one'))
// 2、使用正则
// 替换一个
console.log(str.replace(eval(`/${targetValue}/`), 'two'))
// 全局替换
console.log(str.replace(eval(`/${targetValue}/g`), 'two'))
// 3、回调使用
let result = str.replace(/Microsoft/g, (matchContent) => {
console.log('对匹配的内容', matchContent, '进行操作')
return 'three'
})
console.log(result)
有时候,我们并没有复杂的正则,就像上面的示例一样,此时我们可以使用 replaceAll()方法简化:
// 替换一个
console.log(str.replace('Microsoft', 'one'))
// 替换全部
console.log(str.replaceAll('Microsoft', 'one'))
# 5、matchAll()方法
matchAll() 方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。
知识回顾:迭代对象 🤔
以前我们想要获取匹配的内容元素:
let domR = `
<h1>ren-sir</h1>
<p>ni hao ya!</p>
<p>ge wei lao tie</p>
`
let reg = /<(p)>([\s\S]+?)<\/\1>/gi
// 1、利用组和引用
let contents = []
domR.replace(reg, (v, ...args) => {
contents.push(args[1])
return true
})
console.log(contents) // ["ni hao ya!", "ge wei lao tie"]
现在:
let domR = `
<h1>ren-sir</h1>
<p>ni hao ya!</p>
<p>ge wei lao tie</p>
`
let reg = /<(p)>([\s\S]+?)<\/\1>/gi
// 2、利用matchAll
console.log(domR.match(reg)) // ["<p>ni hao ya!</p>", "<p>ge wei lao tie</p>"]
console.log(domR.matchAll(reg)) // [object RegExp String Iterator] 可遍历
let contents = []
for (const iterator of domR.matchAll(reg)) {
console.log(iterator)
// ["<p>ni hao ya!</p>", "p", "ni hao ya!"]
// ["<p>ge wei lao tie</p>", "p", "ge wei lao tie"]
contents.push(iterator[2])
}
console.log(contents) // ["ni hao ya!", "ge wei lao tie"]
应用示例
let domR = `
<a href="xxxxx">网站1</a>
<a href="xxxxx">网站2</a>
<a href="xxxxx">网站3</a>
`
let reg = /<a.*?href=(['"])(?<link>.*?)\1>(?<title>.*?)<\/a.*>/gi
// console.log(domR.match(reg))
let arr = []
for (const iterator of domR.matchAll(reg)) {
// console.log(iterator)
arr.push(iterator['groups'])
}
console.log(arr)
# 二、正则知识点
参考:
Microsoft Learn (opens new window)
RegExp 对象 (opens new window)
说明
关于正则的内容,一个最直观的感觉就是:学了忘,忘了学 😭。
所以,下面我主要摘取了我常用的知识点。
# 1、字符集
要明确的一点是字符集默认只选其中的一个,要选取多个时可以结合后面的量词使用。
经典应用:
匹配所有字符: [\s\S]、 [\d\D]
① 单个字符
使用示例: /[^-:,\s]+/
表达式 | 描述 |
---|---|
[abc] | 字符集。匹配集合中所含的任一字符。 |
[^abc] | 否定字符集。匹配任何不在集合中的字符。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。 |
应用 ✍:匹配中文
// 方式1
/^\p{sc=Han}+$/gu
// 方式2
/^[\u4e00-\u9fa5]+$/g
let str = `
{张三: 010-88888888,李四: 020-66666666}
`
let newstr = str.match(/[^{}:\s\d-,]+/g).join('') // 去除字符串中的指定内容
console.log(newstr) // "张三李四"
② 多个字符
常见的换行符有:回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)
注意事项 ✍
下面的表达式.
是无法在[]
中起作用的
表达式 | 描述 | 记忆 |
---|---|---|
\w | 匹配任何字母、数字和下划线 | |
\W | 匹配任何非字母、数字和下划线(等价于[^A-Za-z0-9_] ) | |
\d | 匹配任何数字 | 数字(digital) |
\D | 非数字。匹配任何非数字字符(等价于[^0-9] ) | |
\s | 匹配任何空白字符,包括空格、制表符等 | 空白(space) |
\S | 非空白(等价于[^ \t\r\n\v\f] ) | |
. | 除换行符之外的任意字符符 | 在普通单行字符串中,你可以认为它代表所有字符 |
let str = 'lencamo2022'
console.log(str.match(/\d/g)) // ["2", "0", "2", "2"]
console.log(str.match(/\d+/g)) // ["2022"]
console.log(/\s/.test(' \nlencamo')) // true
# 2、字符边界
边界:
提示
关于正则表达式的使用,我们一般是要指定边界来说明从什么地方开始匹配。
如果不使用字符边界,则表示:只要存在并且匹配即可
理解
let str = 'lencamo2022'
console.log(str.match(/^\d$/g)) // null
console.log(str.match(/^\d+$/g)) // null
console.log(/^\s$/.test(' \nlencamo')) // false
表达式 | 描述 |
---|---|
^ | 字符串开头( start )时进行依次匹配 |
$ | 字符串开头( start )时进行依次匹配 |
\b | 单词边界(border)。比如 Sir\b 可以匹配 RenSir 末尾的 Sir,不能匹配 RenSirRen 中的 Sir |
\B | 非单词边界。比如 Ren\B 可以匹配 HelloRenSir 中的 Ren,不能匹配 HelloRen 中的 Ren。 |
// 目标格式电话
let str = '191xxxxxxxx'
// 测试
let one = '19155555555'
let two = '55519155555888'
let three = '19155555555888'
console.log(/191\d{8}/.test(one)) // true
console.log(/191\d{8}/.test(two)) // true
console.log(/191\d{8}/.test(three)) // true
// bug修复
console.log(/^191\d{8}$/.test(two)) // false
console.log(/^191\d{8}$/.test(three)) // false
试一试:
要求邮箱: @ 符前面有 5 到 10 个字符,@ 后面至少 1 个字符,以 com、net、org 结尾
答案
var regExp = /^\w{5,10}@\w+.(com|net|org)$/;
# 3、转义符 ✨
转义符
\
常作用于:-
、.
、\
、/
等
注意
不要忘记我们使用转义符的目的
转义符 与 正则对象
在 RegExp 对象中,正则是字符串 😂 中出现,直接使用\
转义会出现问题:
一些格式化插件会自动将\d 格式化为 d,当出现这种情况我们应该马上知道是怎么回事
// console.log("\d") // "d"
// 解决办法:多加一个\
console.log('\\d') // "\d"
let regex = /^\d+\.\d+$/
let regex = new RegExp('/^\\d+\\.\\d+/$') // 注意这里要要多加一个 \
表达式 | 描述 | 使用示例 |
---|---|---|
\ | 转义字符,比如要匹配+,就要写成\+ 。 | 小数:/^\d+\.\d+$/ |
# 4、量词与分支
默认情况下,量词是贪婪的:
let str = 'abbbbab'
console.log(str.match(/ab+/g)) // ["abbbb", "ab"]
// 禁止贪婪
console.log(str.match(/ab+?/g)) // ["ab", "ab"]
禁止贪婪应用
let domR = `
<span>lencamo</span>
<span>任先生</span>
<span>note-taking</span>
`
let reg1 = /<span>[\s\S]+<\/span>/gi
console.log(domR.match(reg1))
// ["<span>lencamo</span>
// <span>任先生</span>
// <span>note-taking</span>"]
let reg2 = /<span>[\s\S]+?<\/span>/gi
console.log(domR.match(reg2))
// ["<span>lencamo</span>", "<span>任先生</span>", "<span>note-taking</span>"]
let reg3 = /<span>([\s\S]+?)<\/span>/gi
// 1、完整写法
// content表示完整匹配结果,p1表示第一个组
let newDomR = domR.replace(reg3, (v, p1) => {
return `<p>${p1}</p>`
})
console.log(newDomR)
// 2、简写
// $1表示第一个组
let newDomR = domR.replace(reg3, `<p>$1</p>`)
console.log(newDomR)
① 量词
表达式 | 描述 | 记忆 |
---|---|---|
+ | 可重复前面表达式至少 1 次 | 算术赋值运算符+=,起步只是为 1 |
* | 可重复前面表达式 0 次或多次 | 和下面的?类似,只不过*可以表示多个 |
{m} 、{m,} 、{m,n} | 可重复前面表达式指定范围[m,n] 次 | |
…… | …… |
② 分支
其实电话示例部分,还可以使用后面的元字组优化一下:
/(010|020)\-\d{7,8}/
表达式 | 描述 | 使用示例 |
---|---|---|
| | 类似于 or,表示选择 | 电话号码:/010\-\d{7,8} | 020\-\d{7,8}/ |
? | 类似于 TS 中的可选链操作符?,表示可选 | 地址:/^https?:\/\/\w+\.(com | net | org)$/ |
…… | …… |
# 5、预查断言
注意
断言并不参与匹配,只做为正则条件(匹配限制条件)
- 前面/后面必须有什么/不能有什么
应用:密码强度验证
原理:
利用前瞻运算符不会消耗字符的特性
// ?=前面没有字符,始终无法消耗,就一直在初始位置前瞻
// 结果:后面的断言条件可以组成一个交集
let res = /^(?=.*\d)(?=.*[a=z]).*$/
实现:
// 要求:
// - 6-12位
// - 必须包含数字、小写字母、大写字母、特殊字符(@#_中选择)
let reg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#_])[\da-zA-Z@#_]{6,12}$/
应用:
拓展:
// 下面这个正则并不能保证它们都使用了
let reg = /^[\da-zA-Z@#_]{6,12}$/
let str = '任先生不断分享学习笔记,查看任先生的学习笔记提升编程能力'
// "提升"前面的"学习笔记"
let reg = /学习笔记(?=提升)/g
应用:电话号模糊处理
let data = `[
{ "phone": "19155299735" },
{ "phone": "13695292733" }
]`
// ‘有3个数字’后面的那‘8个数字’
let reg = /(?<=\d{3})\d{8}/g
let result = data.replace(reg, (v) => {
return '*'.repeat(8)
})
console.log(result)
// [
// { "phone": "191********" },
// { "phone": "136********" }
// ]
表达式 | 描述 |
---|---|
(?=) | 前瞻断言。比如 Ren(?=Sir) 能匹配 RenSir 中的 Ren,但不能匹配 RenPig 中的 Ren。 |
(?!) | 否定的前瞻断言。比如 Ren(?!Sir) 不能匹配 RenSir 中的 Ren,但能匹配 RenPig 中的 Ren。 |
(?<=) | 后瞻断言。比如(?<=Ren)Sir 能匹配 RenSir 中的 Sir,但不能匹配 ReadSir 中的 Sir。 |
(?<!) | 否定的后瞻断言。比如(?<!Ren)Sir 不能匹配 RenSir 中的 Sir,但能匹配 ReadSir 中的 Sir。 |
# 6、组和引用 ✨
记录组
// 目标: 匹配日期
let str1 = '2022/02/22'
let str2 = '2022-02-22'
// 测试
let demo = '2022-02/22'
console.log(/^\d{4}[\/\-]\d{2}[\/\-]\d{2}$/.test(demo)) // true
// bug修复
console.log(/^\d{4}([\/\-])\d{2}\1\d{2}$/.test(demo)) // false
提示
在需要的时候,我们是可以将原子组 和 量词、分支配合使用的
原子组引用
- 方式 1:使用$
// 目标:DOM元素替换
let domR = `
<h1>lencamo</h1>
<span>任先生</span>
<h2>note-taking</h2>
`
let reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi
// 1、完整写法
// content表示完整匹配结果,p1表示第一个组,p2表示第二个组
let newDomR = domR.replace(reg, (v, p1, p2) => {
return `<p>${p2}</p>`
})
console.log(newDomR)
// 2、简写
// $1表示第一个组,$2表示第二个组
let newDomR = domR.replace(reg, `<p>$2</p>`)
console.log(newDomR)
- 方式 2:使用别名
let domR = `
<h1>lencamo</h1>
<span>任先生</span>
<h2>note-taking</h2>
`
let reg = /<(?<one>h[1-6])>(?<two>[\s\S]*)<\/\1>/gi
domR.replace(reg, (v, ...args) => {
console.log(args)
})
let newDomR = domR.replace(reg, `<p>$<two></p>`)
console.log(newDomR)
- 关于$0
let newDomR = domR.replace(/笔记库/g, `<a href="note-taking.cn">$&</a>`)
console.log(newDomR)
表达式 | 描述 |
---|---|
(expression) | 记录组 |
(?:expression) | 非记录分组(即:无法使用引用的组) |
\num | 对前面所匹配分组的引用(num 表示引用前面的第几个组) |
…… | …… |
非记录组
- 普通
let str = `
https://www.baidu.com
`
// 记录组
let reg1 = /https:\/\/(\w+\.\w+\.(com|org|cn))/i
console.dir(str.match(reg1)) // ["https://www.baidu.com", "www.baidu.com", "com"]
// 非记录组(说白了就是:最后的那个分组我不需要获取,影响我判断)
let reg2 = /https:\/\/(\w+\.\w+\.(?:com|org|cn))/i
console.dir(str.match(reg2)) // ["https://www.baidu.com", "www.baidu.com"]
- 升级(重点关注引用)
let str = `
https://www.baidu.com
http://hahaha.com
https://lishi.com
`
// https的s可有可无(记录)
// www.可有可无(不记录)
let reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|org|cn))/gi
console.dir(str.match(reg)) // ["https://www.baidu.com", "http://hahaha.com", "https://lishi.com"]
# 二、正则速查表
# 1、用户名
- 常规:
/^[a-zA-Z0-9_-]{4,16}$/
(4 到 16 位(字母,数字,下划线,减号))
# 2、邮箱
- 常规:
/^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(?:\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/
# 3、密码
- 弱密码:
/^[\w]{6,16}$/
- 强密码:
/^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@\.#$%^&*? ]).*$/
(最少 6 位,包括至少 1 个大写字母,1 个小写字母,1 个数字,1 个特殊字符)