一、出现的原因
在进行正则匹配的时候,出现了同一个字符串,两次匹配结果不一样的情况
var s1 = "foo";
var s2 = "foo";
var regex = /foo/g;
regex.test(s1); // true
regex.test(s2); // false
在 MDN上有写明
如果正则表达式设置了全局标志,test() 的执行会改变正则表达式 lastIndex属性。连续的执行test()方法,后续的执行将会从 lastIndex 处开始匹配字符串,(exec() 同样改变正则本身的 lastIndex属性值).
var s1 = "foo";
var s2 = "foo";
var regex = /foo/g;
// regex.lastIndex is at 0
regex.test(s1); // true
// regex.lastIndex is now at 3
regex.test(s2); // false
由此可见,导致正则连续匹配出错的原因是全局匹配下生效的lastIndex属性。
二、lastIndex
在讨论之前,先简单回顾一下 JS中正则表达式的使用方式
JS 中正则表达式的使用方式有两种:
第一种是正则表达式对象的方法,常用方法有两个。
- exec(str) : 检索字符串中指定的值。返回找到的值,并确定其位置
- test(str) : 检索字符串中指定的值。返回 true 或 false
第二种是字符串对象的方法,常用方法有四个。
- match(regexp) : 找到一个或多个正则表达式的匹配(该数组的内容依赖于 regexp 是否具有全局标志 g)
- replace(regexp, replacement) : 替换与正则表达式匹配的子串 (replacement可以是字符串或函数、lambda表达式)
- search(regexp) : 检索与正则表达式相匹配的值,返回首次匹配项的索引
- split(search) : 把字符串分割为字符串数组
字符串的全局匹配其实就是依赖方法 RegExp.exec( ) 和 RegExp.test( ) ,它们都以 lastIndex 属性所指的位置作为下次检索的起始点。
这样,就可以通过反复调用这两个方法来遍历一个字符串中的所有匹配文本。
需要注意的是,该属性只有设置标志 g才能使用。
三、解决方案
第一种方案
如上面所述,我们 lastIndex 属性必须要设置 g 标签才能使用。
那么我们在匹配的时候,可以根据情况,直接去掉 g 标签就可以啦。
var s1 = "foo";
var s2 = "foo";
var regex = /foo/;
regex.test(s1); // true
regex.test(s2); // true
第二种方案
很多时候,我们必须要执行 全局匹配( g ),这时候就不能使用第一种方案了。
那么我们可以手动将 lastIndex 重置为 0。
这样,我们再次执行全局匹配的时候,就不会出现 false 的情况了。
var s1 = "foo";
var s2 = "foo";
var regex = /foo/g;
regex.test(s1); // true
regex.lastIndex = 0
regex.test(s2); // true
第三种方案
使用字符串对象的正则匹配方法
var s1 = "foo";
var s2 = "foo";
var s3 = "abb";
var regex = /foo/g;
s1.match(regex) // ['foo']
s2.match(regex) // ['foo']
s3.match(regex) // null
s1.search(regex) // 0
s2.search(regex) // 0
s3.search(regex) // -1