首页 文章

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

提问于
浏览
221

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

6 回答

  • 61

    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'));
    

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

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

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

  • 34

    您正在使用单个 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));
    
  • 9

    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'));
    
  • 0

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

    var re = new RegExp(query, 'gi');
    

    应该

    var re = new RegExp(query, 'i');
    
  • 0

    使用/ g标志告诉它在命中后继续搜索 .

    If the match succeeds, the exec() method returns an array and updates properties of the regular expression object.

    在您第一次搜索之前:

    myRegex.lastIndex
    //is 0
    

    第一次搜索后

    myRegex.lastIndex
    //is 8
    

    删除g并在每次调用exec()后退出搜索 .

  • 283

    我有这个功能:

    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");
    

    第一个电话有效 . 第二个电话没有 . slice 操作抱怨空值 . 我认为这是因为 re.lastIndex . 这很奇怪,因为我希望每次调用函数时都会分配一个新的 RegExp ,而不是在我的函数的多次调用中共享 .

    当我把它改成:

    var re = new RegExp('^([^-]+)-([^-]+)-([^-]+)$', 'g');
    

    然后我没有得到 lastIndex 保持效果 . 它可以像我期望的那样工作 .

相关问题