在以下代码中:
"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 溶液。
在以下代码中:
"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 溶液。
如果您想匹配至少一个这样的“合并”事件,那么您可以执行以下操作:
"a sasas b".match(/s(as)+/g)
如果您想将匹配项作为单独的结果检索,那么您还有更多工作要做;这不是正则表达式旨在处理的情况。基本算法将是:
(为了更有效,您可以匹配偏移量而不是使用子字符串;该技术在此问题中进行了讨论。)
例如,您将从"a sasas b"
. 在第一场比赛之后,你有"sas"
. 取匹配开始后开始一个字符的子字符串,我们将有"asas b"
. 下一个匹配项将在"sas"
此处找到,您将再次使用 重复该过程"as b"
。这将无法匹配,因此您将完成。
这个显着改进的答案归功于@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
,如果设置,也可以导入i
andm
选项。
这是一个新的 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
var match = "a sasas b".match(/s(?=as)/g);
for(var i =0; i != match.length; ++i)
alert(match[i]);
离开 Q. Sheets 的评论和 cdhowie 的响应,我想出了上面的解决方案:它在正则表达式中消耗一个字符,并对匹配字符串的其余部分进行前瞻。通过这两部分,您可以在正则表达式中构造所有位置和匹配字符串。
我希望有一个“检查但不使用”运算符,您可以使用它来在结果中实际包含其余匹配(前瞻)字符串,但不幸的是没有——至少在 JS 中没有。
这是一种通用的方法:
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
,它被解析以进行重叠匹配,但这有点疯狂。