RegEx匹配字符串中的共享部分

时间:2012-11-09 03:52:06

标签: javascript regex pcre

在以下代码中:

"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解决方案。

4 个答案:

答案 0 :(得分:2)

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

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

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

  • 尝试比赛。如果不成功,请停止。
  • 提取您感兴趣的比赛并随意做任何事情。
  • 从匹配中第一个字符后面的一个字符开始,获取原始目标字符串的子字符串。
  • 重新开始,使用此子字符串作为新输入。

(为了提高效率,您可以匹配偏移而不是使用子字符串; this question中讨论了这种技术。)

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

答案 1 :(得分:1)

这个显着改进的答案归功于@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指出没有必要按字符遍历整个字符串;相反,我们可以找到匹配任何地方没有锚点),然后在找到的匹配的索引后继续一个字符。在研究如何检索所述索引时,我发现re.lastIndex属性(exec用于跟踪继续搜索的位置}实际上是可设置!这与我们打算做的事情非常吻合。

需要进一步解释的唯一一点可能就是开始。在没有g标志的情况下,exec可能永远不会返回null(总是返回一个匹配,如果它存在),因此可能进入无限循环。但是,由于match_overlap 按设计寻求多个匹配,我们可以安全地将任何非全局 RegExp重新编译为全局 RegExp,如果设置,也会导入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

答案 2 :(得分:0)

var match = "a sasas b".match(/s(?=as)/g);

for(var i =0; i != match.length; ++i)
    alert(match[i]);

通过Q. Sheets和cdhowie的回复,我想出了上面的解决方案:它在正则表达式中消耗了一个字符并对匹配字符串的其余部分进行了预测。通过这两个部分,您可以在正则表达式中构造所有位置和匹配的字符串。

我希望有一个“检查但不消耗”的运算符,你可以用来在结果中实际包含匹配(lookahead)字符串的其余部分,但遗憾的是不存在 - 至少在JS中没有

答案 3 :(得分:0)

这是一种通用方式:

​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,因为他或她通常会{ {1}}。从这里我们想要创建一个新的match对象锚定在开头(以防止重复的重叠匹配 - 这部分可能没有意义,除非你自己遇到问题 - 不要担心它)。然后,我们只是匹配主题字符串RegExp的每个子字符串,并将结果推送到一个数组,如果非空,则返回该数组(否则返回this)。请注意,如果用户传入已经锚定的表达式,那么本质上是错误的 - 首先我剥离了锚点,但后来我意识到我正在做一个假设用户的代替,我们应该避免。最后,可以更进一步,并以某种方式将得到的匹配数组合并为一个匹配结果,类似于null选项通常会出现的结果;并且可以进一步甚至并组成一个新的标志,例如 //g被解析以进行重叠匹配,但这有点疯狂。