正则第二次匹配和第一次不一致,正则循环中匹配无效

正则匹配不准确问题

复现场景(建议可以打开控制台试下):

let reg = /J/gi

for (let i = 0; i <= 10; i++) {
  console.log(reg.test('Jioho'))
}

// true 和 false 交替输出

是不是认为是循环的锅?来个非循环的场景

let reg = /i/gi
console.log(reg.test('Jioho')) // true
console.log(reg.test('Jioho')) // false
console.log(reg.test('Jioho')) // true
console.log(reg.test('Jioho')) // false
console.log(reg.test('Jioho')) // true

为什么正则第二次匹配无效

是因为正则表达式中的 g ,循环匹配的问题

正则的 lastIndex 从字面上来讲就是最后一个索引,实际上它的意思是正则表达式开始下一次查找的索引位置,第一次的时候总是为 0 的,第一次查找完了的时候会把 lastIndex 的值设为匹配到得字符串的最后一个字符的索引位置加 1,第二次查找的时候会从 lastIndex 这个位置开始,后面的以此类推。如果没有找到,则会把 lastIndex 重置为 0。要注意的是,lastIndex 属性只有在有全局标志正则表达式中才有作用

所以上面的例子中 reg 只声明了一次,可是使用了多次所以才出现那种情况

如果代码改成这样,输出就符合预期了:

// 1. 去除 g
let reg = /j/i
console.log(reg.test('Jioho'))
console.log(reg.test('Jioho'))
console.log(reg.test('Jioho'))

// 2. 循环中重新创建正则
for (let i = 0; i <= 10; i++) {
  const reg = /j/gi
  console.log(reg.test('Jioho'))
}

深入了解正则 Regex

在 MDN 文档有详细的介绍:RegExp(正则表达式)

看到今天的主角:RegExp.lastIndex

lastIndex 是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。

注意是可读可写的。而且查看详细的介绍规则:

只有正则表达式使用了表示全局检索的 “g” 标志时,该属性才会起作用。此时应用下面的规则:

  • 如果 lastIndex 大于字符串的长度,则 regexp.test 和 regexp.exec 将会匹配失败,然后 lastIndex 被设置为 0。
  • 如果 lastIndex 等于字符串的长度,且该正则表达式匹配空字符串,则该正则表达式匹配从 lastIndex 开始的字符串。(then the regular expression matches input starting at lastIndex.)
  • 如果 lastIndex 等于字符串的长度,且该正则表达式不匹配空字符串 ,则该正则表达式不匹配字符串,lastIndex 被设置为 0.。
  • 否则,lastIndex 被设置为紧随最近一次成功匹配的下一个位置。

文档中还介绍到了一个 RegExp.prototype.compile() 运行脚本的期间(重新)编译正则表达式。

所以我们可以在循环中或者在特定业务逻辑下,重新编译正则表达式

最后

关于正则多次匹配无效的问题可能的原因就找到了,因为使用了 g 循环匹配的标记,而且正则在使用一次后没有重新编译,可是之前有已经有相关的匹配了,lastInedx 不为 0

要解决这种问题方法也很多

  • 每次都重新声明正则表达式
for (let i = 0; i <= 10; i++) {
  const reg = /j/gi
  console.log(reg.test('Jioho'))
}
  • 重置 lastIndex
const reg = /j/gi
for (let i = 0; i <= 10; i++) {
  reg.lastIndex = 0
  console.log(reg.test('Jioho'))
}
  • 重新编译正则
const reg = /j/gi
for (let i = 0; i <= 10; i++) {
  reg.compile()
  console.log(reg.test('Jioho'))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值