Javascript:如何在RegEx .exec结果中获得多个匹配项

时间:2012-06-29 23:52:12

标签: javascript regex node.js v8

当我跑步时

/(a)/g.exec('a a a ').length

我得到了

2

但我认为它应该返回

3

因为字符串中有3个a,而不是2!

为什么?

我希望能够在RegEx中搜索字符串的所有出现并迭代它们。

FWIW:我正在使用node.js

7 个答案:

答案 0 :(得分:34)

exec()仅返回第一个匹配的捕获集,而不是您期望的匹配集。所以你真正看到的是$0(整个匹配,“a”)和$1(第一次捕获) - 即。长度为2. exec()的数组同时设计为可以再次调用 来获取 next 匹配的捕获。来自MDN

  

如果正则表达式使用“g”标志,则可以多次使用exec方法在同一个字符串中查找连续匹配。执行此操作时,搜索从正则表达式的lastIndex属性指定的str的子字符串开始(test也将提升lastIndex属性)。

答案 1 :(得分:22)

您可以改为使用match

'a a a'.match(/(a)/g).length  // outputs: 3

答案 2 :(得分:7)

你只匹配第一个a。长度为2的原因是它找到第一个匹配和第一个匹配的括号组部分。在你的情况下,他们是相同的。

考虑这个例子。

var a = /b(a)/g.exec('ba ba ba ');
alert(a);

输出ba, a。数组长度仍为2,但更明显的是发生了什么。 “ba”是全场比赛。 a是带括号的第一个分组匹配。

MDN documentation支持这一点 - 只返回第一个匹配和包含的组。要查找所有匹配项,您可以使用mVChr所述的match()。

答案 3 :(得分:4)

代码:

alert('a a a'.match(/(a)/g).length);

输出:

3

答案 4 :(得分:3)

while循环可以帮助您

x = 'a a a a';
y = new RegExp(/a/g);
while(null != (z=y.exec(x))) {
   console.log(z);     // output: object
   console.log(z[0]);  // ouput: "a"
}

如果您添加计数器,那么您将获得它的长度。

x = 'a a a a';
counter = 0;
y = new RegExp(/a/g);
while(null != (z=y.exec(x))) {
   console.log(z);     // output: object
   console.log(z[0]);  // output: "a"
   counter++;
}
console.log(counter);  // output: 4

这是非常安全的,即使它没有找到任何匹配,它只是退出而计数器将为0

主要目的是告诉如何使用RegExp循环并从相同匹配的RegExp的字符串中获取所有值

答案 5 :(得分:1)

regexp.exec(str)返回第一个匹配项或整个匹配项以及第一次捕获(re = /(a)/g;时),如其他答案中所述

const str = 'a a a a a a a a a a a a a';
const re = /a/g;

const result = re.exec(str);
console.log(result);

但是它也记住regexp.lastIndex属性中的位置。

下一个呼叫从regexp.lastIndex开始搜索并返回下一个匹配项。

如果没有更多匹配项,则regexp.exec返回null,并将regexp.lastIndex设置为0。

const str = 'a a a';
const re = /a/g;

const a = re.exec(str);
console.log('match : ', a, ' found at : ', re.lastIndex);

const b = re.exec(str);
console.log('match : ', b, ' found at : ', re.lastIndex);

const c = re.exec(str);
console.log('match : ', c, ' found at : ', re.lastIndex);

const d = re.exec(str);
console.log('match : ', d, ' found at : ', re.lastIndex);

const e = re.exec(str);
console.log('match : ', e, ' found at : ', re.lastIndex);

这就是为什么您可以使用while循环,当匹配为null

时会停止的原因

const str = 'a a a';
const re = /a/g;

while(match = re.exec(str)){
  console.log(match, ' found at : ', match.index); 
}

答案 6 :(得分:0)

就您的示例而言,.match() 是您的最佳选择。但是,如果确实需要子组,则可以创建生成器函数。

function* execAll(str, regex) {
  if (!regex.global) {
    console.error('RegExp must have the global flag to retrieve multiple results.');
  }

  let match;
  while (match = regex.exec(str)) {
    yield match;
  }
}

const matches = execAll('a abbbbb no match ab', /\b(a)(b+)?\b/g);
for (const match of matches) {
  console.log(JSON.stringify(match));
  let otherProps = {};
  for (const [key, value] of Object.entries(match)) {
    if (isNaN(Number(key))) {
      otherProps[key] = value;
    }
  }
  
  console.log(otherProps);
}

虽然大多数 JS 程序员认为污染原型是不好的做法,但您也可以将其添加到 RegExp.prototype

if (RegExp.prototype.hasOwnProperty('execAll')) {
  console.error('RegExp prototype already includes a value for execAll.  Not overwriting it.');
} else {
  RegExp.prototype.execAll = 
    RegExp.prototype = function* execAll(str) {
      if (!this.global) {
        console.error('RegExp must have the global flag to retrieve multiple results.');
      }

      let match;
      while (match = this.exec(str)) {
        yield match;
      }
    };
}

const matches = /\b(a)(b+)?\b/g.execAll('a abbbbb no match ab');
console.log(Array.from(matches));