正则表达式 replace 过程解析

replace 过程解析

思考下面的结果:

console.log("more".replace(/.*/g, "p")); // pp
console.log("more".replace(/.*?/g, "p")); // pmpoprpep

上面分别使用贪婪模式和懒惰模式尝试替换,都出现了意外的结果:

  1. 贪婪模式,多出一个p
  2. 懒惰模式,没有替换任何字符

前置须知:

  1. .可以匹配任意字符,包括空字符。

  2. 正则匹配时,人为字符串首尾及每个字符间隙都存在一个空字符用于匹配。

  3. 正则对象中有一个很重要的属性 lastIndex,表示下次匹配的开始。

    1. 一般在使用正则对象的方法时使用,字符串的replace方法中也用到了它。

replace 执行的过程:

  1. 当正则配置了global修饰符时,replace会从头查找字符串。
    1. 直到匹配结果为 null 时结束(正则匹配不到结果时返回 null )
  2. 开始之前会先创建一个空的 results 数组,用于存放匹配的结果。
  3. 过程中,每匹配到一个结果,就会向results数组中添加结果
  4. 同时 lastIndex 更新为结果的下一个字符位置,再继续匹配
    1. 如果匹配的结果是空字符(字符间隙),则位置+1
    2. 这是神奇的地方,如果匹配到字符间隙,就会跳过本应匹配的字符
  5. 匹配结束后会替换匹配的结果

整个过程类似:

String.prototype.myReplace = function (reg, rpStr) {
  let str = this.valueOf()
  let global = reg.global
  let results = []
  let done = false
  let history = {}, step = 0 // 流水
  while (!done) {
    // 从上次匹配结果位置开始
    // 这里记录本次匹配的字符串范围,用于打印
    let newStr = str.slice(reg.lastIndex)
    // exec会更新lastIndex
    let result = reg.exec(str)
    if (result === null) {
      done = true // 匹配结束
    } else {
      results.push(result)
      if (!global) {
        done = true // 非全局匹配,匹配一次结束
      } else {
        if (!result[0]) {
          // 如果匹配到的是'',字符间隙,nextIndex额外+1
          reg.lastIndex = reg.lastIndex + 1
        }
      }
    }
    history[`step${++step}:`] = {
      str: newStr,
      result: result ? result[0] : result,
      results: results.map(r => r[0]),
      lastIndex: reg.lastIndex,
      done: done
    }
  }
  
  console.table(history)

  // 替换results的结果,并返回结果
  // ...
}

('more').replace(/.*/g, 'p') 匹配记录:

(index)strresultresultslastIndexdone
step1:‘more’‘more’[ ‘more’ ]4false
step2:‘’‘’[ ‘more’, ‘’ ]5false
step3:‘’null[ ‘more’, ‘’ ]0true

所以替换 [ ‘more’, ‘’ ] 后,结果为 pp

('more').replace(/.*?/g, 'p') 匹配记录:

(index)strresultresultslastIndexdone
step1:‘more’‘’[ ‘’ ]1false
step2:‘ore’‘’[ ‘’, ‘’ ]2false
step3:‘re’‘’[ ‘’, ‘’, ‘’ ]3false
step4:‘e’‘’[ ‘’, ‘’, ‘’, ‘’ ]4false
step5:‘’‘’[ ‘’, ‘’, ‘’, ‘’, ‘’ ]5false
step6:‘’null[ ‘’, ‘’, ‘’, ‘’, ‘’ ]0true

所以替换 [ ‘’, ‘’, ‘’, ‘’, ‘’ ] 后,结果为 pmpoprpep

PS:
上面模拟的代码,在直接匹配边界(\b \B)时会有问题。
模拟的代码主要用于理解匹配过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值