Tim*_*gus 6 javascript regex perl pcre
为什么以下正则表达式(通过捕获组)在Javascript中捕获字符串'abc',但在PCRE中不捕获(尽管它仍然匹配)?
(.*)*
这就是PCRE中捕获组为空的原因:
初始状态
(.*)* abc
^ ^
Run Code Online (Sandbox Code Playgroud)首先,(.*)匹配部件abc,输入位置前进到最后.捕获组包含abc此时.
(.*)* abc
^ ^
Run Code Online (Sandbox Code Playgroud)现在,输入位置之后的c字符,剩余的输入为空字符串.Kleene明星开始第二次尝试匹配该(.*)组:
(.*)* abc
^ ^
Run Code Online (Sandbox Code Playgroud)该(.*)组匹配空字符串后abc.由于匹配,先前捕获的字符串将被覆盖.
由于输入位置没有前进,因此在*那里迭代并且匹配成功.
JS和PCRE之间的行为差异是由于指定了正则表达式引擎的方式.PCRE的行为与Perl一致:
PCRE:
$ pcretest
PCRE version 8.39 2016-06-14
re> /(.*)*/
data> abc
0: abc
1:
Run Code Online (Sandbox Code Playgroud)
Perl的:
$ perl -e '"abc" =~ /(.*)*/; print "<$&> <$1>\n";'
<abc> <>
Run Code Online (Sandbox Code Playgroud)
当捕获组第二次匹配时,.NET会将捕获的值添加到捕获堆栈.Perl和PCRE将简单地覆盖它.
至于JavaScript:
这是ECMA- 262§21.2.2.5.1运行时语义:RepeatMatcher抽象操作:
抽象操作RepeatMatcher采用八个参数,Matcher
m,整数min,整数(或∞)max,布尔值greedy,状态x,Continuationc,整数parenIndex和整数parenCount,并执行以下步骤:
- 如果
max为零,则返回c(x).- 创建一个内部Continuation闭包
d,它接受一个State参数y并在评估时执行以下步骤:
- 一个.如果
min为零且y'sendIndex等于x'sendIndex,则返回failure.- 湾 如果
min为零,min2则为零; 否则令min2是min?1.- C.如果
max是∞,则为max2∞; 否则令max2是max?1.- d.调用
RepeatMatcher(m, min2, max2, greedy, y, c, parenIndex, parenCount)并返回其结果.- 让我们
cap成为x捕获列表的新副本.- 对于每一个整数
k,其满足parenIndex < k并k ? parenIndex+parenCount设置cap[k]到undefined.- 我们
e要x的endIndex的.- 让我们
xr成为国家(e, cap).- 如果
min不为零,则返回m(xr, d).- 如果
greedy是false,那么
- 一个.打电话
c(x)让它z成为结果.- 湾 如果
z不是failure,请返回z.- C.调用
m(xr, d)并返回其结果.- 打电话
m(xr, d)让它z成为结果.- 如果
z不是failure,请返回z.- 调用
c(x)并返回其结果.
这基本上是对量词进行评估时应该发生什么的定义.RepeatMatcher是处理内部操作匹配的操作m.
你还需要了解一个国家是什么(§21.2.2.1,强调我的):
状态是有序对(
endIndex,captures),其中endIndex是整数,捕获是NcapturingParens值列表.状态用于表示正则表达式匹配算法中的部分匹配状态.的endIndex是一加到目前为止由图案匹配的最后一个输入字符的索引,而captures保持捕获括号的结果.第nth个元素captures是一个List,表示由第一n组捕获括号获得的值,如果n尚未到达第二组捕获括号,则为undefined .由于回溯,许多国家可能在匹配过程中随时使用.
对于您的示例,RepeatMatcher参数是:
m:Matcher负责处理子模式(.*)min:0(Kleene星形量词的最小匹配数)max:∞(Kleene星形量词的最大匹配数)greedy:true(使用的Kleene星形量词是贪婪的)x:( (0, [undefined])见上面的州定义)c:延续,此时它将是:在折叠父规则后,一直将其State参数作为成功返回的ContinuationMatchResultparenIndex:0(根据§21.2.2.5,这是在此生产的左侧发生的整个正则表达式中的左捕获括号的数量)parenCount:1(相同的spec段落,这是此生产的Atom扩展中左侧捕获括号的数量 - 我不会在此处粘贴完整的规范,但这基本上意味着m定义了一个捕获组)规范中的正则表达式匹配算法是根据连续传递方式定义的.基本上,这意味着c操作意味着接下来会发生什么.
让我们展开这个算法.
在第一次传球时,x1状态是(0, [undefined]).
max 不是零d1,它将在第二遍中使用,所以我们稍后会再回到这一点.cap1cap1到undefined,这是一个无操作在拳头通e1 = 0xr1 =(e1,cap1)min 为零,跳过此步骤greedy 是的,跳过这一步z1 = m(xr,d1) - 这将评估子模式(.*)现在,让我们退一步一点- m将匹配(.*)对abc,然后调用我们的d1封,所以让我们解开那一个.
d1与状态评价y1 = (3, ["abc"]):
min是0,但y1的endIndex3和x1的endIndex是0,所以不回failuremin2= min= 0,因为min= 0max2= max=∞自max=∞RepeatMatcher(m, min2, max2, greedy, y, c, parenIndex, parenCount)并返回其结果.即:RepeatMatcher(m,0,∞,false,y 1,c,0,1)所以,现在我们要进行第二次迭代,其中x2 = y1 = (3, ["abc"]).
max 不是零d2在这一点上cap2["abc"]cap2到undefined,我们得到cap2 =[undefined]e2 = 3xr2 =(e2,cap2)min 为零,跳过此步骤greedy 是的,跳过这一步设z2 = m(xr2,d2) -此评估子模式(.*)
这个时间m将与之后的空字符串匹配abc,并用那个调用我们的d2个闭包.让我们来评估d2的作用.它的参数是y2 =(3, [""])
min仍然是0,但是y2的endIndex是3和x2的endIndex是也3(记住,这时间x是y从先前的迭代的状态)中,封闭简单地返回failure.
z2是failure,跳过这一步c(x2),c((3, ["abc"]))在此迭代中.c只需返回一个有效的MatchResult,就像我们在模式的末尾一样.这意味着d1返回此结果,并且第一次迭代返回从步骤10传递它.
基本上,正如您所看到的,导致JS行为与PCRE不同的规范行如下:
一个.如果
min为零且y'sendIndex等于x'sendIndex,则返回failure.
结合使用时:
- 调用
c(x)并返回其结果.
如果迭代失败,则返回先前捕获的值.