为什么带有全局标志的RegExp会产生错误的结果?

abo*_*out 254 javascript regex

当我使用全局标志和不区分大小写的标志时,这个正则表达式有什么问题?查询是用户生成的输入.结果应该是[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]
Run Code Online (Sandbox Code Playgroud)

var reg = /^a$/g;
for(i = 0; i++ < 10;)
   console.log(reg.test("a"));
Run Code Online (Sandbox Code Playgroud)

Ion*_*tan 326

RegExp对象跟踪lastIndex匹配发生的位置,因此在后续匹配时它将从最后使用的索引开始,而不是0.看看:

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));

alert(re.lastIndex);

result.push(re.test('Foo Bar'));
Run Code Online (Sandbox Code Playgroud)

如果您不想lastIndex在每次测试后手动重置为0,只需删除该g标志即可.

这是规范规定的算法(第15.10.6.2节):

RegExp.prototype.exec(串)

对正则表达式执行字符串的正则表达式匹配,并返回包含匹配结果的Array对象;如果字符串不匹配,则返回null.搜索字符串ToString(string)以查找正则表达式模式的出现,如下所示:

  1. 设S为ToString(string)的值.
  2. 设长度为S的长度.
  3. 设lastIndex为lastIndex属性的值.
  4. 让我成为ToInteger(lastIndex)的值.
  5. 如果全局属性为false,则让i = 0.
  6. 如果I <0或I> length,则将lastIndex设置为0并返回null.
  7. 调用[[Match]],给它参数S和i.如果[[Match]]返回失败,请执行步骤8; 否则让r成为其状态结果并转到步骤10.
  8. 设i = i + 1.
  9. 转到第6步.
  10. 设e是r的endIndex值.
  11. 如果global属性为true,则将lastIndex设置为e.
  12. 设n是r的捕获数组的长度.(这与15.10.2.1的NCapturingParens值相同.)
  13. 返回具有以下属性的新数组:
    • index属性设置为完整字符串S中匹配的子字符串的位置.
    • input属性设置为S.
    • length属性设置为n + 1.
    • 0属性设置为匹配的子字符串(即,在offset i包含和offset e exclusive之间的S部分).
    • 对于每个整数i,使得I> 0且I≤n,将名为ToString(i)的属性设置为r的captures数组的第i个元素.

  • 这就像Hitchhiker的Galaxy API设计指南."你陷入困境的陷阱已经在规范中完整记录了几年,如果你只是打扰检查" (75认同)
  • Firefox的粘性标志根本不是你所暗示的.相反,它就好像在正则表达式的开头有一个^,除了这个^匹配*current*string位置(lastIndex)而不是字符串的开头.你有效地测试正则表达式是否匹配"就在这里"而不是"在lastIndex之后的任何地方".查看您提供的链接! (4认同)

Roa*_*rth 68

您正在使用单个RegExp对象并多次执行它.在每次连续执行时,它从最后一个匹配索引继续.

您需要"重置"正则表达式,以便在每次执行之前从头开始:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]
Run Code Online (Sandbox Code Playgroud)

已经说过每次创建一个新的RegExp对象可能更具可读性(无论如何,由于RegExp被缓存,开销很小):

result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));
Run Code Online (Sandbox Code Playgroud)

  • 或者干脆不使用“g”标志。 (2认同)

Jam*_*mes 34

RegExp.prototype.test更新正则表达式的lastIndex属性,以便每个测试将从最后一个测试停止的位置开始.我建议使用,String.prototype.match因为它不更新lastIndex属性:

!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true
Run Code Online (Sandbox Code Playgroud)

注意:!!将其转换为布尔值,然后反转布尔值,以便反映结果.

或者,您可以重置该lastIndex属性:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
Run Code Online (Sandbox Code Playgroud)


小智 10

删除全局g标志将解决您的问题.

var re = new RegExp(query, 'gi');
Run Code Online (Sandbox Code Playgroud)

应该

var re = new RegExp(query, 'i');
Run Code Online (Sandbox Code Playgroud)