【问题标题】:Shared part in RegEx matched stringRegEx 匹配字符串中的共享部分
【发布时间】:2012-10-29 09:45:40
【问题描述】:

在以下代码中:

"a sasas b".match(/sas/g) //returns ["sas"]

字符串实际上包括两个sas 字符串,a [sas]as ba sa[sas] b

如何修改 RegEx 以匹配两者?

另一个例子:

"aaaa".match(/aa/g); //actually include [aa]aa,a[aa]a,aa[aa]

请考虑一般问题,而不仅仅是以上实例。

首选纯 RexEx 解决方案。

【问题讨论】:

标签: javascript regex pcre


【解决方案1】:

如果您想匹配至少一个这样的“合并”事件,那么您可以执行以下操作:

"a sasas b".match(/s(as)+/g)

如果您想将匹配项检索为单独结果,那么您还有一些工作要做;这不是正则表达式旨在处理的情况。基本算法是:

  • 尝试匹配。如果不成功,请停止。
  • 提取您感兴趣的匹配项,然后随心所欲地使用它。
  • 获取原始目标字符串的子字符串,从匹配中第一个字符后的一个字符开始。
  • 重新开始,使用这个子字符串作为新的输入。

(为了更高效,您可以匹配偏移量而不是使用子字符串;该技术在this question 中进行了讨论。)

例如,您可以以"a sasas b" 开头。在第一场比赛之后,你有"sas"。取匹配开始后一个字符开始的子字符串,我们将得到"asas b"。下一场比赛将在此处找到"sas",您将再次使用"as b" 重复该过程。这将无法匹配,因此您将完成。

【讨论】:

    【解决方案2】:

    这个显着改进的答案归功于@EliGassert。

    String.prototype.match_overlap = function(re)
        {
            if (!re.global)
                re = new RegExp(re.source,
                                'g' + (re.ignoreCase ? 'i' : '')
                                    + (re.multiline  ? 'm' : ''));
            var matches = [];
            var result;
            while (result = re.exec(this))
                matches.push(result),
                re.lastIndex = result.index + 1;
            return matches.length ? matches : null;
        }
    

    @EliGassert 指出不需要逐个字符遍历整个字符串;相反,我们可以在 anywhere 找到匹配项( 不使用锚点),然后在找到的匹配项的索引之后继续一个字符。在研究如何检索所述索引时,我发现 exec 使用的 re.lastIndex 属性来跟踪它应该继续搜索的位置实际上是可设置!这非常适合我们打算做的事情。

    唯一需要进一步解释的可能是开头。在没有g 标志的情况下,exec 可能永远不会返回null(如果存在,则总是返回它的一个匹配项),因此可能会进入无限循环。然而,由于match_overlap 设计 寻求多个匹配,我们可以安全地将任何非全局 RegExp 重新编译为全局 @987654330 @,导入im 选项(如果已设置)。

    这是一个新的 jsFiddle:http://jsfiddle.net/acheong87/h5MR5/

    document.write("<pre>");
    document.write('sasas'.match_overlap(/sas/));
    document.write("\n");
    document.write('aaaa'.match_overlap(/aa/));
    document.write("\n");
    document.write('my1name2is3pilchard'.match_overlap(/[a-z]{2}[0-9][a-z]{2}/));
    document.write("</pre>");​
    

    输出:

    sas,sas
    aa,aa,aa
    my1na,me2is,is3pi
    

    【讨论】:

      【解决方案3】:
      var match = "a sasas b".match(/s(?=as)/g);
      
      for(var i =0; i != match.length; ++i)
          alert(match[i]);
      

      离开 Q. Sheets 的评论和 cdhowie 的响应,我想出了上面的解决方案:它在正则表达式中消耗一个字符,并对匹配字符串的其余部分进行前瞻。有了这两部分,你就可以在你的正则表达式中构造所有的位置和匹配的字符串了。

      我希望有一个“检查但不使用”运算符,您可以使用它来在结果中实际包含其余匹配(前瞻)字符串,但不幸的是没有——至少在 JS 中没有.

      【讨论】:

        【解决方案4】:

        这是一种通用方法:

        ​String.prototype.match_overlap = function(regexp)
            {
                regexp = regexp.toString().replace(/^\/|\/$/g, '');
                var re = new RegExp('^' + regexp);
                var matches = [];
                var result;
                for (var i = 0; i < this.length; i++)
                    if (result = re.exec(this.substr(i)))
                        matches.push(result);
                return matches.length ? matches : null;
            }
        

        用法:

        var results = 'sasas'.match_overlap(/sas/);
        

        返回:

        • (重叠)匹配的array,或null

        示例:

        这是a jsFiddle,其中:

        document.write("<pre>");​
        document.write('sasas'.match_overlap(/sas/));
        document.write("\n");
        document.write('aaaa'.match_overlap(/aa/));
        document.write("\n");
        document.write('my1name2is3pilchard'.match_overlap(/[a-z]{2}[0-9][a-z]{2}/));
        document.write("</pre>");​
        

        返回这个:

        sas,sas
        aa,aa,aa
        my1na,me2is,is3pi
        

        说明:

        稍微解释一下,我们打算让用户将RegExp 对象 传递给这个新函数match_overlap,就像他或她通常对match 所做的那样。从这里我们想创建一个新的RegExp对象,锚定在开头(以防止重复的重叠匹配——这部分可能没有意义,除非你自己遇到问题——别担心)。然后,我们简单地匹配主题字符串this 的每个子字符串并将结果推送到一个数组,如果非空则返回该数组(否则返回null)。请注意,如果用户传入一个已经锚定的表达式,这本质上是错误的——一开始我去掉了锚,但后来我意识到我在做一个假设用户的替代,我们应该避免。最后,可以更进一步,以某种方式将生成的匹配数组合并为一个匹配结果,类似于//g 选项通常会发生的情况;并且可以进一步甚至制作一个新标志,eg //o 被解析以进行重叠匹配,但这有点疯狂。 p>

        【讨论】:

        • 如果我错了,请纠正我,但你每次都在走整个字符串。这在长字符串上可能会变得非常昂贵。您最好搜索单个实例并重新开始(设置i)到最后一个匹配的第一个字符的索引(+1)。我看到你在比赛中做 ^,所以它至少会很快短路。我还没有进行性能检查以确定这是否真的有影响,但我至少想提出来。
        • @EliGassert - 你是绝对正确的。我单独发布了一个新的解决方案。我不想投票赞成你对这个问题的回答,因为我觉得这不是一个通用解决方案。但是,我想通过查看您过去的一些答案并对我认为对个人有用和/或有趣的任何答案进行投票来表达我的谢意。
        猜你喜欢
        • 2014-07-17
        • 2012-06-15
        • 2019-10-18
        • 1970-01-01
        • 1970-01-01
        • 2017-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多