【问题标题】:Javascript: How to get multiple matches in RegEx .exec resultsJavascript:如何在 RegEx .exec 结果中获得多个匹配项
【发布时间】:2012-06-29 23:52:11
【问题描述】:

当我跑步时

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

我明白了

2

但我认为它应该返回

3

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

这是为什么呢?

我希望能够在 RegEx 中搜索所有出现的字符串并对其进行迭代。

FWIW:我正在使用 node.js

【问题讨论】:

标签: javascript regex node.js v8


【解决方案1】:

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

如果您的正则表达式使用“g”标志,您可以多次使用 exec 方法在同一字符串中查找连续匹配项。当您这样做时,搜索将从正则表达式的 lastIndex 属性指定的 str 的子字符串开始(测试也将推进 lastIndex 属性)。

【讨论】:

  • 顺便说一句,整个匹配在js中是$&
  • 有趣,我什至不知道JS中有这样的$变量。我只是在用 Perl 说话。 :) 不过很高兴知道。
  • @Qtax,你说的是替换方法还是其他上下文?
  • @rambo,仅在替换字符串中。
【解决方案2】:

您可以改用match

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

【讨论】:

  • 确实,match 是这种情况下的理想工具,前提是 OP 不需要每次匹配中的子组。
  • 只要总是至少有一个匹配项。否则 match 返回 null 而不是空数组。
【解决方案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 字符串中获取所有值

【讨论】:

  • +1 我认为这是最好的解决方案。 .exec 返回一些.match 没有返回的额外属性,它意味着被迭代调用以返回每个增量匹配。仅供参考,您可以从 while 循环中删除 null != ,因为 null 值将是虚假的并退出循环。另外,快乐赏金宾果游戏!
  • @KyleMit,感谢您的赏金!是的,我们可以删除null !=。但我觉得这对少数新手了解代码流会有帮助。
  • 这个非常有效的解决方案。然而,我确实使用没有'g' 标志(全局)的 RegExp 尝试了这个。不使用会产生无限循环。
  • @andiOak -- 是的,删除 'g' 标志将进入无限循环,因为每次搜索指针都会重新指向起点 0。
  • y = /a/g 就足够了,因为它已经是一个正则表达式。调用new RegExp() 是多余的。
【解决方案4】:

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

考虑这个例子。

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

它输出ba, a。数组长度仍然是 2,但更明显的是发生了什么。 "ba" 是完全匹配。 a 是括号内的第一个分组匹配。

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

【讨论】:

    【解决方案5】:

    代码:

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

    输出:

    3
    

    【讨论】:

      【解决方案6】:

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

      这就是为什么您可以使用当匹配为null 时停止的while 循环

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

      【讨论】:

        【解决方案7】:

        对于您的示例,.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));

        【讨论】:

          【解决方案8】:

          已经有几个答案,但不必要地复杂。对结果的身份检查过多,因为它始终是数组或null

          let text = `How much wood would a woodchuck chuck if a woodchuck could chuck wood?`;
          let re = /wood/g;
          let lastMatch;
          
          while (lastMatch = re.exec(text)) {
            console.log(lastMatch);
            console.log(re.lastIndex);
          
            // Avoid infinite loop
            if(!re.global) break;
          }
          

          您可以将无限循环保护移到条件表达式中。

          while (re.global && (lastMatch = re.exec(text))) {
           console.log(lastMatch);
           console.log(re.lastIndex);
          }
          

          【讨论】:

            【解决方案9】:

            封装成实用函数:

            const regexExecAll = (str: string, regex: RegExp) => {
              let lastMatch: RegExpExecArray | null;
              const matches: RegExpExecArray[] = [];
            
              while ((lastMatch = regex.exec(str))) {
                matches.push(lastMatch);
            
                if (!regex.global) break;
              }
            
              return matches;
            };
            

            用法:

            const matches = regexExecAll("a a a", /(a)/g);
            
            console.log(matches);
            

            输出:

            [
              [ 'a', 'a', index: 0, input: 'a a a', groups: undefined ],
              [ 'a', 'a', index: 2, input: 'a a a', groups: undefined ],
              [ 'a', 'a', index: 4, input: 'a a a', groups: undefined ]
            ]
            

            【讨论】:

              猜你喜欢
              • 2012-04-27
              • 2016-10-08
              • 2017-01-08
              • 1970-01-01
              • 2016-08-29
              • 2021-06-05
              • 2010-11-08
              • 2022-11-04
              • 1970-01-01
              相关资源
              最近更新 更多