Javascript中的正则表达式不够贪婪?

5

我已经编写了一个简单的代码来捕获字符串中的某个组:

/[a-z]+([0-9]+)[a-z]+/gi    (n chars , m digts , k chars).

code :

var myString='aaa111bbb222ccc333ddd';
var myRegexp=/[a-z]+([0-9]+)[a-z]+/gi;

var match=myRegexp.exec(myString);
console.log(match)
 
 while (match != null)
{
  match = myRegexp.exec(myString);
  console.log(match)
}

结果如下:
["aaa111bbb", "111"]
["ccc333ddd", "333"]
null

等一下,为什么他没有尝试 bbb222ccc 部分呢?

我的意思是,它看到了 aaa111bbb ,然后应该尝试 bbb222ccc ...(这很贪心!)

我错过了什么吗?

另外

看着...

   while (match != null)
    {
      match = myRegexp.exec(myString);
      console.log(match)
    }

如何进展到第二个结果的呢?最初是这样的:

var match=myRegexp.exec(myString);

稍后(在while循环中):
match=myRegexp.exec(myString);
match=myRegexp.exec(myString);

这是同一行......它在哪里记得第一个结果已经显示了?


8
因为第一个匹配后的索引位于第一个匹配的末尾,所以“bbb”已经被跳过了,除了剩余的字符串“ccc333ddd”外,没有任何可匹配的内容。贪婪模式意味着“+”将尝试尽可能多地匹配字符串,而不考虑正则表达式中下一部分的匹配情况。 - Esailija
嗨@Esailija,是的,我已经明白了。但如果它像它所说的那样贪婪,那就不是这样。 - Royi Namir
@Esailija请将您的评论粘贴为答案。 - Royi Namir
@Esailija,你能否请看一下我的编辑? - Royi Namir
1
@RoyiNamir 这是一种贪婪的方式——但它不会返回并重新检查已经匹配的表达式部分。 - garyh
2个回答

4

.exec 方法在使用 g 标志时是有状态的。该状态保存在正则表达式对象的 .lastIndex 属性中。

var myString = 'aaa111bbb222ccc333ddd';
var myRegexp = /[a-z]+([0-9]+)[a-z]+/gi;
var match = myRegexp.exec(myString);
console.log(myRegexp.lastIndex); //9, so the next `.exec` will only look after index 9
while (match != null) {
    match = myRegexp.exec(myString);
    console.log(myRegexp.lastIndex);
}

状态可以通过将 .lastIndex 设置为 0 或执行不同的字符串来重置。例如,re.exec("") 将重置状态,因为状态是针对 'aaa111bbb222ccc333ddd' 保留的。
对于 .test 方法也适用同样的规则,如果您不想出现意外情况,请不要在用于 .test 的正则表达式中使用 g 标志。请参阅 https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec

这是否意味着,如果我没有将正则表达式存储在变量中,并始终使用原始的/[a-z]+([0-9]+)[a-z]+/,它不会记住索引? - Royi Namir
2
@RoyiNamir 是的,当你创建一个新的正则表达式对象时,它还没有任何状态。换句话说,/[a-z]+([0-9]+)[a-z]+/gi.lastIndex === 0 总是成立的。 - Esailija
我不太理解“test”这一部分。为什么我不能和[g]一起使用test?是因为它会产生多个结果吗? - Royi Namir
因为g标志使.exec.test有状态地工作。如果您删除g标志,则会注意到在使用.exec进行匹配后,lastIndex为0。 - Esailija

2
你也可以手动更新lastIndex属性:
var myString='aaa111bbb222ccc333ddd';
var myRegexp=/[a-z]+([0-9]+)[a-z]+/gi;

var match=myRegexp.exec(myString);
console.log(match);

 while (match != null)
{
  myRegexp.lastIndex -= match[0].length - 1; // Set the cursor to the position just after the beginning of the previous match
  match = myRegexp.exec(myString);
  console.log(match)
}

请参考此链接:MDN exec

编辑:

顺便提一下,您的正则表达式应为:/[a-z]{3}([0-9]{3})[a-z]{3}/gi


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