1

在以下代码中:

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

该字符串实际上包括两个sas字符串,a [sas]as b并且a sa[sas] b.

如何修改 RegEx 以匹配两者?

另一个例子:

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

请考虑一般问题,而不仅仅是上述情况。

首选纯 RexEx 溶液。

4

4 回答 4

2

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

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

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

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

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

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

于 2012-11-09T03:58:03.830 回答
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,如果设置,也可以导入iandm选项。

这是一个新的 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
于 2012-11-09T21:21:39.703 回答
0
var match = "a sasas b".match(/s(?=as)/g);

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

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

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

于 2012-11-09T04:16:37.287 回答
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.

例子:

这是一个 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选项通常会发生的情况;并且可以更进一步并组成一个新标志,例如 //o,它被解析以进行重叠匹配,但这有点疯狂。

于 2012-11-09T04:45:20.247 回答