提问人:about 提问时间:10/5/2009 最后编辑:zb226about 更新时间:7/27/2022 访问量:63316
为什么带有全局标志的正则表达式会给出错误的结果?
Why does a RegExp with global flag give wrong results?
问:
当我使用全局标志和不区分大小写标志时,这个正则表达式有什么问题?查询是用户生成的输入。结果应为 [true, true]。
var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]
var reg = /^a$/g;
for(i = 0; i++ < 10;)
console.log(reg.test("a"));
答:
您正在使用单个对象并多次执行它。在每次连续执行时,它从上次匹配索引继续。RegExp
您需要在每次执行之前将正则表达式“重置”为从头开始:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]
话虽如此,每次创建一个新的 RegExp 对象可能更具可读性(开销很小,因为无论如何都会缓存 RegExp):
result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));
评论
g
RegExp.prototype.test
更新正则表达式的 lastIndex
属性,以便每个测试都将从最后一个测试停止的地方开始。我建议使用 String.prototype.match
,因为它不会更新 lastIndex
属性:
!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true
注意: !!
将其转换为布尔值,然后反转布尔值以反映结果。
或者,您可以重置 lastIndex
属性:
result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
带有标志的对象会跟踪发生匹配的 lastIndex
,因此在后续匹配中,它将从上次使用的索引开始,而不是从 0 开始。看一看:RegExp
g
var query = 'Foo B';
var re = new RegExp(query, 'gi');
console.log(re.lastIndex);
console.log(re.test('Foo Bar'));
console.log(re.lastIndex);
console.log(re.test('Foo Bar'));
console.log(re.lastIndex);
如果您不想在每次测试后手动重置为 0,只需删除该标志即可。lastIndex
g
以下是规范规定的算法(第 15.10.6.2 节):
RegExp.prototype.exec(字符串)
执行 字符串的正则表达式匹配 针对正则表达式和 返回一个 Array 对象,其中包含 匹配结果,如果 string did not match 字符串 ToString(string) 用于搜索 正则表达式的出现 模式如下:
- 设 R 是这个 RexExp 对象。
- 设 S 是 ToString(string) 的值。
- 设 length 为 S 的长度。
- 设 lastIndex 是 R 上 lastIndex 属性的值。
- 设 i 是 ToInteger(lastIndex) 的值。
- 如果全局属性为 false,则 i = 0。
- 如果 i < 0 或 i >长度,则将 R 的 lastIndex 属性设置为 0 并返回 null。
- 调用 [[Match]],为其提供参数 S 和 i。如果 [[匹配]] 返回失败,转到步骤 9; 否则设 r 为其状态结果 ,然后转到步骤 10。
- 设 i = i+1。
- 转到步骤 7。
- 设 e 是 r 的 endIndex 值。
- 如果 global 属性为 true,则将 R 的 lastIndex 属性设置为 e。
- 设 n 是 r 的捕获数组的长度。(这是一样的 值为 15.10.2.1 NCapturingParens.)
- 返回具有以下属性的新数组:
- 指数 属性设置为 完整中匹配的子字符串 字符串 S。
- 设置了 input 属性 到 S。
- length 属性设置为 n + 1。
- 0 属性设置为 匹配的子字符串(即 S 介于偏移量 i 和 偏移 e 独家)。
- 对于每个 整数 i,使得 i > 0,我≤ n, 将名为 ToString(i) 的属性设置为 R 的第 i 个元素捕获数组。
评论
lastIndex
删除全局标志将解决您的问题。g
var re = new RegExp(query, 'gi');
应该是
var re = new RegExp(query, 'i');
使用 /g 标志会告知它在命中后继续搜索。
如果匹配成功,exec() 方法将返回一个数组并更新正则表达式对象的属性。
在您第一次搜索之前:
myRegex.lastIndex
//is 0
第一次搜索后
myRegex.lastIndex
//is 8
删除 g,它会在每次调用 exec() 后退出搜索。
评论
exec
我有这个功能:
function parseDevName(name) {
var re = /^([^-]+)-([^-]+)-([^-]+)$/g;
var match = re.exec(name);
return match.slice(1,4);
}
var rv = parseDevName("BR-H-01");
rv = parseDevName("BR-H-01");
第一个调用有效。
第二个调用没有。该操作抱怨 null 值。我认为这是因为.这很奇怪,因为我希望每次调用函数时都会分配一个新的函数,而不是在函数的多个调用之间共享。slice
re.lastIndex
RegExp
当我把它改成:
var re = new RegExp('^([^-]+)-([^-]+)-([^-]+)$', 'g');
然后我没有得到滞留效应。它的工作方式与我所期望的一样。lastIndex
您需要设置 re.lastIndex = 0,因为使用 g 标志正则表达式跟踪上次发生的匹配,因此 test 不会去测试相同的字符串,因为您需要执行 re.lastIndex = 0
var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
re.lastIndex=0;
result.push(re.test('Foo Bar'));
console.log(result)
评论
re