Skip to content

04捕获

正则捕获: 把一个字符串中和正则匹配的部分获取到

方法:

[正则] exec test [字符串] replace split match


基于exec可以实现正则的捕获

  1. 如果当前正则和字符串不匹配,捕获的结果是null
  2. 如果匹配,捕获的结果是一个数组 0: 大正则捕获的内容 index: 正则捕获的起始索引 input: 原始操作的字符串
  3. 执行一次exec只能捕获到第一个和正则匹配的内容,其余匹配内容没捕获到(正则捕获有懒惰性)

解决正则捕获的懒惰性,需要加全局修饰符g

编写一个 捕获所有匹配的方法

js
RegExp.prototype._execAll = function(str) {
  // 为防止出现死循环,检查一下正则是否有 g,没有只把第一次捕获的结果返回
  if (!this.global) return this.exec(str)

  let ret = []
  let valArr = this.exec(str)
  while (valArr) {
    // 或者用 this.lastIndex < str.length来判断
    // 把每次捕获到的结果(数组)的第一项 存储到ret中
    ret.push(valArr[0])
    valArr = this.exec(str)
  }
  return ret
}

其实 match 也就是上面的方法的实现


分组捕获

在正则捕获时,如果存在分组,捕获时匹配大正则字符,且还会把小分组匹配内容也拿到

?: 可以阻止分组捕获 (合理使用提高正捕获性能)

正则捕获还具有贪婪性,每一次捕获时,总是捕获和正则匹配中最长的内容

阻止贪婪捕获 \d+? 加个 ?即可 (把问号放到量词元字符后面,取消捕获的贪婪性)


? 问号在正则中的作用

  1. 量词元字符 出现 0 次或 1 次 /a?/ a出现1次或不出现
  2. 取消正则捕获的贪婪性 /\d+?/ 捕获时只捕获最短的内容
  3. 只匹配不捕获 /?😕
  4. ?= 正向预查
  5. ?! 负向预查

test匹配也相当于捕获,修改了lastIndex的值 。

坑:

js
let str = 'hello2018'
let reg = /\d+/g
if (reg.test(str)) {
  console.log(reg.exec(str)) // null
}

虽然捕获的不是同一个字符串,但正则是同一个时. 上次正则处理时修改了它的lastIndex 所以会对下一次匹配的新字符串产生影响

js
let str = 'hello2018'
let reg = /\d+/g
console.log(reg.exec(str)) // ['2018' ... ]
console.log(reg.exec('hello2018world666')) // ['666' ...]

RegExp.$1 把上一次匹配 test/exec 到的结果获取到,获取的是第一个小分组匹配的内容,大正则匹配的内容无法获取。 这是一个全局的值,浏览器中$1只有一个,其他正则操作会覆盖该值,所以这种方式没实际用处。


replace 实现正则捕获的方法 (本身是字符串的替换方法)

js
let str = 'newA2018newB0606'
str = str.replace('new', 'newT')
// str -> "newTA2018newB0606"
str = str.replace('new', 'newT')
// str -> "newTTA2018newB0606"
str = 'newA2018newB0606'
str = str.replace(/new/g, 'N')
// str -> "NA2018NB0606"

用reg正则和str字符串进行匹配,匹配几次就替换几次,每一次都是把当前大正则匹配的结果用第二个传递的字符串替换掉

js
let str = 'hello{val:2018}world{val:2019}'
let reg = /\{val:(\d+)\}/g
str = str.replace(reg, '@')
// str -> "hello@world@"
str = 'hello{val:2018}world{val:2019}'
// $1不是拿这个字符串替换掉大正则匹配的内容
// 此处的$1代表第一个分组匹配的内容,等价于 RegExp.$1
str = str.replace(reg, '$1')
// str -> "hello2018world2019"
  1. reg和str匹配多次,函数就会被触发多少次,而且传递了一些参数值
  2. 每一次arg中存储的信息,和执行exec捕获的信息相似 (内置原理:每一次正则匹配到结果,都把函数执行,然后基于exec把本次匹配的信息捕获到,然后把捕获的信息传递给这个函数)
  3. 每一次函数中返回是什么,就把当前大正则匹配的内容替换成什么
js
let str = 'hello{val:2018}world{val:2019}'
let reg = /\{val:(\d+)\}/g
str = str.replace(reg, (...arg) => {
  console.log(arg)
  return ''
})
// ["{val:2018}", "2018", 5, "hello{val:2018}world{val:2019}"]
// ["{val:2019}", "2019", 20, "hello{val:2018}world{val:2019}"]
// "helloundefinedworldundefined"

共 20 个模块,1301 篇 Markdown 文档。