具有全局选项的正则表达式测试连续调用失败

13

我整天都在尝试解决这个问题,但我无法确定是我做错了什么还是我发现了Chrome的JavaScript引擎中的一个bug。看起来,对于同一个输入字符串,在使用全局标志的RegExp对象连续调用会返回不一致的结果。我正在使用以下函数进行测试:

function testRegex(pattern, array) {
    document.writeln('Pattern = ' + pattern + ', Array = ' + array + '<br/>');
    for (var ii = 0; ii < array.length; ii++) {
        document.writeln(ii + ', ');
        document.writeln(array[ii] + ', ');
        document.writeln(pattern.test(array[ii]) + '<br />');
    }
    document.writeln('<br/>');
}
当我使用/a/g作为模式,并对各种字符串数组调用该函数时,我得到了以下结果,其中许多结果在我看来都是不正确的:
// EXPECTED: True
// ACTUAL:   True
testRegex(/a/g, ['a']);

// EXPECTED: True,  True
// ACTUAL:   True,  False 
testRegex(/a/g, ['a', 'a']);

// EXPECTED: True, True,  True
// ACTUAL:   True, False, True
testRegex(/a/g, ['a', 'a', 'a']);

// EXPECTED: True, False, True
// ACTUAL:   True, False, True
testRegex(/a/g, ['a', 'b', 'a']);

// EXPECTED: True, True,  True, True
// ACTUAL:   True, False, True, False
testRegex(/a/g, ['a', 'a', 'a', 'a']);

// EXPECTED: True, False, False, True
// ACTUAL:   True, False, False, True   
testRegex(/a/g, ['a', 'b', 'b', 'a']);
当我使用相同的字符串数组调用同一函数,但将/a/作为模式传递时,实际结果都与预期结果匹配。
// EXPECTED: True
// ACTUAL:   True
testRegex(/a/, ['a']);

// EXPECTED: True, True
// ACTUAL:   True, True
testRegex(/a/, ['a', 'a']);

// EXPECTED: True, True, True
// ACTUAL:   True, True, True
testRegex(/a/, ['a', 'a', 'a']);

// EXPECTED: True, False, True
// ACTUAL:   True, False, True
testRegex(/a/, ['a', 'b', 'a']);

// EXPECTED: True, True, True, True
// ACTUAL:   True, True, True, True
testRegex(/a/, ['a', 'a', 'a', 'a']);

// EXPECTED: True, False, False, True
// ACTUAL:   True, False, False, True
testRegex(/a/, ['a', 'b', 'b', 'a']);

我已经创建了上面代码的一个可行示例:http://jsfiddle.net/FishBasketGordo/gBWsN/

我有什么遗漏吗?不管模式是否全局,给定的字符串数组结果不应该是相同的吗?请注意,我主要在Chrome中工作,但在Firefox 4和IE 8中也观察到类似的不正确结果。

2个回答

20

如果你按照以下方式更改你的测试循环:

for (var ii = 0; ii < array.length; ii++) {
    document.writeln(ii + ', ');
    document.writeln(array[ii] + ', ');
    document.writeln(pattern.test(array[ii]) + '<br />');
    pattern.lastIndex = 0;
}

如果您不使用"g"标志,那么您的代码将能够正常工作。问题是"g"标志导致RegExp对象陷入困境。由于"g"标志,第一次迭代循环后,"lastIndex"的值被设置为1。如果您不将其重置以重新开始搜索,则它会认为在第二次调用时,您要求它从偏移量1继续前进。

在".replace()"调用之外使用正则表达式的"g"标志具有奇怪的语义含义。


我今天早上的目标是最终使用 replace 中的 RegExp,这就是为什么我一开始使用全局选项的原因,但像我的 RegExp 一样,我卡在了中间步骤。 - FishBasketGordo

9
这不是一个错误,而是一个特性。你得到的结果并不是“错误”的,只是意料之外的。
RegExp实例属性
每个RegExp对象都有五个属性。source属性是一个只读字符串,包含正则表达式的文本。global属性是一个只读布尔值,指定正则表达式是否具有g标志。ignoreCase属性是一个只读布尔值,指定正则表达式是否具有i标志。multiline属性是一个只读布尔值,指定正则表达式是否具有m标志。最后一个属性是lastIndex,一个可读写的整数。对于具有g标志的模式,此属性存储下一次搜索应该开始的位置。它由exec()和test()方法使用,如前一节所述。 源代码

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接