1. 常用元字符
为正则表达式中的最小单元,表示1个字符
.
任意一个字符,除了换行和行结束符- 用于多行匹配时的流程
- 如果在没有匹配到任何字符的情况下,遇到换行符,则继续向下一行匹配
- 如果在已经匹配到任何字符的情况下,遇到换行符,则终止匹配
- 使用
s
模式,可以将多行文本被视为单行文本匹配 - 使用
g
模式,在已经匹配到的情况下,遇到换行符,则当前匹配结束,将继续向下开始下一次匹配,多行记录会匹配出多条结果。再看到这句话的时候可能忘记了,用多行文本测试下即可
- 用于多行匹配时的流程
\w
标识大写字母、小写字母和下划线、汉字等非特殊字符的字符。取反为\W
^
代表字符串的开始(前边可以存在空字符),在中括号中,代表否$
代表字符串的结尾,不是要匹配的结尾\s
代表空字符、换行符、tab缩进等空白符。取反为\S
\d
代表0-9任意1个数值。 取反为\D
2. 模式(修正符)
- i 忽略大小写
- g 全局匹配,匹配所有,而非匹配一次就停止匹配,多次成功匹配将返回多个结果
- m 多行匹配,每一行单独匹配处理,每一行都可以应用
起始和结束
符来控制哟 - s 将允许
.
匹配换行符。 - u 使用unicode码的模式进行匹配。如果遇到宽字节字符,匹配后可能乱码,就需要使用此模式
- y模式,相较于g模式,y模式在某些情况下更有效率,其工作方式为,匹配连续符合条件的记录,若在待检测字符串中,遇到了不满足条件的(不算第一次),则会停止检索,而g模式会一直查找到文档的末尾。y模式适用于匹配连续出现的记录。
3. 转义符
\
为转义字符,在字面量时正常使用即可,但是在new RegExp
时,里边的转义符需要写两个\\
4. 起始和结束
用^
和$
表示,如表单验证中,限制4-6位的数字字母下划线/^\w{3,6}$/
5. 逻辑或
用竖线 |
表示,意为|
左右两侧表达式或的关系。
6. 量词(数量)(重复匹配)
+
1~n个字符,贪婪匹配。 同{1,}
*
0~n个字符,贪婪匹配,匹配到最后一个符合条件的。同{0,}
?
问号前一个字符的0~1次重复,本身也是贪婪匹配,贪婪到自己限定的1个字符。很多时候需要使用它来禁止贪婪。结束禁止的应用如下:+?
使加号禁止贪婪,向最小范围倾斜,也就是1位*?
使星号禁止贪婪,向最小范围倾斜,也就是0位{2,}?
同理,应用到2-n范围时,使其向最小范围倾斜,也就时2位,匹配到2位后即结束匹配??
0位
{3,5}
3-5个重复匹配
6.1 禁止贪婪
上边提到了禁止贪婪,接下来让我测试下是不是和说的一样
'jaxjaxjax'.match(/\w*?/)
// ['', index: 0, input: 'jaxjaxjax', groups: undefined]
'jaxjaxjax'.match(/\w*/)
// ['jaxjaxjax', index: 0, input: 'jaxjaxjax', groups: undefined]
上例中验证了向最小范围倾斜的问题,那么这有什么意义呢?让我们再用一个例子说明一下:
需求:我们希望匹配html的开始标签,也就是此例中的<div>
'<div>jax</div>'.match(/[\s\S]*>/)
// 如此匹配就匹配到了所有结果
// ['<div>jax</div>', index: 0, input: '<div>jax</div>', groups: undefined]
// 但是这个结果并不是我们想要的,我们只想要 <div> 标签
// 此时就需要用到了禁止贪婪(当然也有别的方法,此例主要解释贪婪的问题)
'<div>jax</div>'.match(/[\s\S]*?>/)
// 只需要加一个?,结果就是我们的预期结果了
通过例子我们会发现
● 若?禁止贪婪后边没有其他条件了,那么就是它限定的就是最小范围,就像例一中的空字符串一样。
● 若?禁止贪婪后边仍然有其他条件匹配上了,那么他限定的范围就是从开始匹配,到下一个匹配开始之前的位置。
这在我们大范围检索时非常有用。
7. 元字表 - 区间匹配
7.1 区间匹配
[a-zA-Z_0-9]
相当于\w
匹配范围内任意一个范围内的字符[\s\S]+
可以巧妙的匹配所有字符,比如匹配span
标签内的所有字符,标签内是多行文本,使用此方法可以快速匹配
7.2 取反匹配
当起始符^
在区间中时,代表非(取反)的意思。
[^a-zA-Z_0-9]
意为匹配任意一个不是 a-z、A_-Z、、0-9 的字符张三
要取出张三,可以使用/[^\s]+/g
。意为除了空的都要
7.3 特殊字符
()
小括号出现在原子表中时,那么那就代表单纯的小括号.
+
出现在原子表中时,他也只代表字符本身,并没有什么特殊含义
8. 字符属性
通过\p
{unicode属性值}
配合u
模式一起使用
它们可匹配字母、数字、符号、标点符号、空格
能匹配的属性太多了,此处只举几个例子,具体使用查看官网
- 查找字母
/\p{L}/gu
- 查找汉字
/\p{sc=Han}/gu
语言系统有很多,此例为中文- sc为Scripts的缩写,具体查看官网https://unicode.org/reports/tr24/#Script
9. 方法
9.1 字符串方法
9.1.1 replace
小括号在replace
时捕获
'a,b,c,d,e'.replace(/(\w)/g, function(findStr, $1, index) {
// 此时每完成一次匹配,就会执行一次这个函数
// $1 代表捕获到的内容
// 在这个例子中,这个函数将会被执行5次
// 如果是 /(\w),(\w)/g的话,每次匹配两个,这个函数将被执行2次
})
// 捕获多个
'a,b,c,d,e'.replace(/(\w),(\w)/, function(findStr, $1, $2, index) {
// 共捕获2个
// $1 = a, $2 = b
return '111'
})
// 给网址添加上https和www.前缀
let link = 'http://jax.com'
let reg = /(http)s?(:\/\/)(www\.)?/i
let linked = link.replace(reg, (_, ...args) => {
args[0] += 's'
args[2] = args[2] || 'www.'
return args.slice(0, 3).join('')
})
9.1.2 split
小括号在split
时的捕获,会将捕获内容动态设置成分割器,很好用哟
'<div>我买了一个{{ thing }},好{{ mood }}啊。</div>'
.split(/(\{{1,2}\s?.*?\s?\}{1,2})/)
/**
* 分割后的内容
0: "<div>我买了一个"
1: "{{ thing }}"
2: ",好"
3: "{{ mood }}"
4: "啊。</div>"
*/
9.1.3 match
match()
方法,在不用模式g
时,会返回匹配的详细信息,如:
let str = 'liujax'
str.match(/\w/)
// 返回详细信息
// ['l', index: 0, input: 'liujax', groups: undefined]
如果使用g
模式,则会丢失详细信息,如:
let str = 'liujax'
str.match(/\w/g)
// 返回信息
// ['l', 'i', 'u', 'j', 'a', 'x']
那么,如果我们在做全局匹配时,仍然想要获取详细信息该怎么做呢?这时就需要使用正则方法exec()
了
- 小括号在
match
时捕获
'34[abc]12'.match(/^(\d+)\[/)
// 结果为
['34[', '34', index: 0, input: '34[abc]', groups: undefined]
// 结果中第1位,成功捕获到了(\d+)括号中的34
9.1.4 matchAll 匹配全局
需配合g模式
使用,返回一个完整匹配对象的迭代器,如获取标签中的内容,可以使用:
let reg = /<(h[1-6])>([\s\S]+?)<\/\1>/gi
// 该方式就把标签的完成信息返回了,并拿不到每次匹配的完成信息
// console.log(tags.match(reg))
// [('<h1>jax</h1>', '<h1>zll</h1>')]
// 使用matchAll返回每次匹配的迭代器,能拿到每次匹配的完整信息
let matched = tags.matchAll(reg)
for (const iterator of matched) {
console.log(iterator[2])
// jax
// zll
}
9.1.5 手动实现一个matchAll
String.prototype.matchAll = function(reg) {
let ret = this.match(reg)
if (ret) {
let str = this.replace(ret[0], '^'.repeat(ret[0].length))
return [ret, ...(str.matchAll(reg) || [])]
}
}
9.1.6 search
str.search
相当于数组的includes
(返回值),查询给定字符(正则)是否在目标字符串中
9.2 正则方法
9.2.1 exec
配合g模式
使用时,可以逐条返回详情,使用方法如下:
let str = 'liujax'
let reg = /\w/g // 正则要声明在此处哟
let res = null
while(res = reg.exec(str)) {
// 此时会把每一次的匹配信息,包含详细信息,全都返回回来
console.log(res)
// ['l', index: 0, input: 'liujax', groups: undefined]
// ['i', index: 1, input: 'liujax', groups: undefined]
}
正则要声明在循环外部,使用全局模式时,每次匹配的信息都会存储在这个正则对象上,比如lastIndex
等,如果每次匹配都重新声明,那么永远匹配第一个结果。
使用exec
时,可以手动调整reg.lastIndex
属性,来改变正则的起始检索位置,可配合y模式
提高检索效率。
9.2.2 test
返回true/false
10. 元字组
用小括号()
表示,也代表分组。
10.1 在正则表达式中使用分组结果
let tel = '2022/02/02'
let reg = /^\d{4}([\/-])\d{1,2}\1\d{1,2}/
console.log(tel.match(reg))
注意正则中的后半部分的\1
意为前边分组的结果,这么写就要求日期字符串的分隔符前后一致,前边匹配到了/,那么\1
就表示/。
10.2 替换html标签
将h标签替换成p标签
let str = `
<h1>jax</h1>
<h2>zll</h2>
`
let reg = /<(h[1-6])>([\s\S]+)<\/\1>/gi
// 使用字符串方式返回
let res = str.replace(reg, `<p>$2</p>`)
// 使用函数方式返回
let res = str.replace(reg, (findStr, $1, $2) => {
return `<p>${$2}</p>`
})
10.3 不记录组(忽略组)
使用方式(?:\w+)
,将?:
放在原子组的开头,则表示这个组的结果是不需要的,当有使用括号,但是又不想无用分组过多时,可以使用。比如我们经常用到的|
,需要用括号包裹,但是包裹的内容分组本身又不会被使用,可以用此方式去掉。
10.4 别名
使用方式:
- (?./*?):使用?<>声明别名,使用的时候就是
$<alias>
- 使用别名后,正则的完整对象中的
groups
就会有值了
11. 批量使用正则
适用于多条规则的验证,如验证密码的需求,要求密码是5-10位的数字或字母,并且至少有一位大写字母,我们可以这样写:
let pwd = 'abc123,'
const regs = [
/^[a-z0-9]{5,10}$/i,
/[A-Z]/
]
regs.every(reg => reg.test(pwd))
12. 快速替代符号
'=jax-'.replace(/jax/, "$`$'$&")
// '==-jax-'
// 比如,我想让字符串的左右符号各扩展两个,可以这么做
'=jax-'.replace(/jax/, "$`$`$&$'$'")
// '===jax---'
- $` 代表这匹配字符的左侧,此例中为=
- $’ 代表这匹配字符的右侧 ,此例中为-
- $& 代表匹配的内容,也就是正则表达式中的
\0
所以,此例(“
‘
`
‘'$&”)就是在匹配字符串jax
的左侧,添加了 =-
场景一:
- 将给定字符串的某个字符添加链接,可使用$&快速替换
13. 断言匹配
断言是正则表达式的条件语句,断言虽然放在括号中,但是它只是相当于if条件,并不再组中,也只参与判断,并不在匹配结果中
13.1 (?=) 后边(右边)是什么
取反为 **(?!)**
后边不是什么。
匹配字符串后边的条件。举个例子,我想给 Jax 后边是ZLL 的Jax标个颜色,那么,后边是ZLL就是断言条件,我们来看一下怎么写:
let str = 'jax1 jax2 jaxZLL'
let reg = /jax(?=ZLL)/g
str = str.replace(reg, `<span style="color: red;">$&</span>`)
// 'jax1 jax2 <span style="color: red;">jax</span>ZLL'
例一,给字符串中的金额添加小数位.00
let goods = `
a,300元,10个
b,200.00元,5个
c,100元,300个
`
// 给金额添加小数位
let reg = /\d+?(\.\d+)?(?=元)/g
let ret = goods.replace(reg, (_, $1) => {
return $1 ? _ : _ + '.00'
})
13.2 (?<=) 前边(左边)是什么
取反为**(?<!)**
,前边不是什么
(?<=jax)\d,意思是取数字前边是jax的数字,前边条件断言
例一,脱敏电话号
let phone = '188888888888'
// 我们来脱敏后4位
let reg = /(?<=\d{7})\d{4}/gi
phone = phone.replace(reg, '****')
console.log(phone)
今天正则的分享就到这里
循序渐进,不忘初心,我们明天见
有问题请留言
或发邮件: liujax@126.com