Javascropt 正则表达式整理

官网
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F

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模式一起使用
它们可匹配字母、数字、符号、标点符号、空格
能匹配的属性太多了,此处只举几个例子,具体使用查看官网

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值