加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Perl vs Javascript正则表达式

发布时间:2020-12-15 22:04:45 所属栏目:大数据 来源:网络整理
导读:为什么以下正则表达式(通过捕获组)在 Javascript中捕获字符串’abc’,但在PCRE中不捕获(尽管它仍然匹配)? (.*)* 解决方法 这就是PCRE中捕获组为空的原因: 初始状态 (.*)* abc ^ ^ 首先,(.*)部分与abc匹配,输入位置前进到结尾.捕获组此时包含abc. (.*)* abc
为什么以下正则表达式(通过捕获组)在 Javascript中捕获字符串’abc’,但在PCRE中不捕获(尽管它仍然匹配)?

(.*)*

解决方法

这就是PCRE中捕获组为空的原因:

>初始状态

(.*)*     abc
 ^        ^

>首先,(.*)部分与abc匹配,输入位置前进到结尾.捕获组此时包含abc.

(.*)*     abc
    ^        ^

>现在,输入位置在c字符之后,其余输入是空字符串. Kleene星开始第二次尝试匹配(.*)组:

(.*)*     abc
 ^           ^

>(.*)组匹配abc后的空字符串.由于匹配,先前捕获的字符串将被覆盖.
>由于输入位置没有前进,*结束迭代,匹配成功.

JS和PCRE之间的行为差??异是由于指定了正则表达式引擎的方式. PCRE的行为与Perl一致:

PCRE:

$pcretest
PCRE version 8.39 2016-06-14

  re> /(.*)*/
data> abc
 0: abc
 1:

Perl的:

$perl -e '"abc" =~ /(.*)*/; print "<$&> <$1>n";'
<abc> <>

设compare this with .NET,它具有相同的行为,但支持多次捕获:

当捕获组第二次匹配时,.NET会将捕获的值添加到捕获堆栈. Perl和PCRE将简单地覆盖它.

至于JavaScript:

这里是ECMA-262§21.2.2.5.1运行时语义:RepeatMatcher抽象操作:

The abstract operation RepeatMatcher takes eight parameters,a Matcher m,an integer min,an integer (or ∞) max,a Boolean greedy,a State x,a Continuation c,an integer parenIndex,and an integer parenCount,and performs the following steps:

  1. If max is zero,return c(x).
  2. Create an internal Continuation closure d that takes one State argument y and performs the following steps when evaluated:
    • a. If min is zero and y‘s endIndex is equal to x‘s endIndex,return failure.
    • b. If min is zero,let min2 be zero; otherwise let min2 be min?1.
    • c. If max is ∞,let max2 be ∞; otherwise let max2 be max?1.
    • d. Call RepeatMatcher(m,min2,max2,greedy,y,c,parenIndex,parenCount) and return its result.
  3. Let cap be a fresh copy of x‘s captures List.
  4. For every integer k that satisfies parenIndex < k and k ≤ parenIndex+parenCount,set cap[k] to undefined.
  5. Let e be x‘s endIndex.
  6. Let xr be the State (e,cap).
  7. If min is not zero,return m(xr,d).
  8. If greedy is false,then
    • a. Call c(x) and let z be its result.
    • b. If z is not failure,return z.
    • c. Call m(xr,d) and return its result.
  9. Call m(xr,d) and let z be its result.
  10. If z is not failure,return z.
  11. Call c(x) and return its result.

这基本上是对量词进行评估时应该发生什么的定义. RepeatMatcher是处理内部操作m的匹配的操作.

你还需要了解一个国家是什么(§21.2.2.1,强调我的):

A State is an ordered pair (endIndex,captures) where endIndex is an integer and captures is a List of NcapturingParens values. States are used to represent partial match states in the regular expression matching algorithms. The endIndex is one plus the index of the last input character matched so far by the pattern,while captures holds the results of capturing parentheses. The nth element of captures is either a List that represents the value obtained by the nth set of capturing parentheses or undefined if the nth set of capturing parentheses hasn’t been reached yet. Due to backtracking,many
States may be in use at any time during the matching process.

对于您的示例,RepeatMatcher参数是:

> m:负责处理子模式的匹配器(.*)
> min:0(Kleene星形量词的最小匹配数)
> max:∞(Kleene星形量词的最大匹配数)
>贪婪:真实(使用的Kleene星形量词是贪婪的)
> x:(0,[undefined])(参见上面的状态定义)
> c:延续,此时它将是:在折叠父规则后,一直将其State参数作为成功的MatchResult返回的Continuation
> parenIndex:0(根据§21.2.2.5,这是在左边的整个正则表达式中左捕获括号的数量)
生产)
> parenCount:1(相同的spec段落,这是该生产的Atom扩展中左侧捕获括号的数量 – 我不会在此处粘贴完整的规范,但这基本上意味着m定义了一个捕获组)

规范中的正则表达式匹配算法是根据continuation-passing style定义的.基本上,这意味着c操作意味着接下来应该发生什么.

让我们展开这个算法.

第一次迭代

在第一次传递时,x1状态为(0,[undefined]).

> max不为零
>我们在此时创建了延续闭包d1,它将在第二遍中使用,所以我们稍后会再回到这一点.
>制作捕获列表的副本cap1
>将cap1中的捕捉重置为未定义,这是第一遍中的无操作
>设e1 = 0
>设xr1 =(e1,cap1)
> min为零,跳过此步骤
>贪婪是真的,跳过这一步
>设z1 = m(xr,d1) – 这会计运算符模式(.*)

现在让我们退一步 – m将匹配(.*)对抗abc,然后调用我们的d1闭包,所以让我们展开那个.

用状态y1 =(3,[“abc”])评估d1:

> min为0,但y1的endIndex为3,x1的endIndex为0,所以不要返回失败
>设min 2 = min = 0,因为min = 0
>设max2 = max =∞,因为max =∞
>调用RepeatMatcher(m,parenCount)并返回其结果.那就是:RepeatMatcher(m,∞,false,y1,1)

第二次迭代

所以,现在我们要进行第二次迭代,x2 = y1 =(3,[“abc”]).

> max不为零
>此时我们创建了延续闭包d2
>制作捕获列表[“abc”]的副本cap2
>将cap2中的捕获重置为undefined,我们得到cap2 = [undefined]
>设e2 = 3
>设xr2 =(e2,cap2)
> min为零,跳过这一步
>设z2 = m(xr2,d2) – 这会评估子模式(.*)

这次m将匹配abc之后的空字符串,并用那个调用我们的d2闭包.让我们来评估d2的作用.它的参数是y2 =(3,[“”])

min仍为0,但y2的endIndex为3,x2的endIndex也为3(请记住,此时x是前一次迭代的y状态),闭包只会返回失败.
> z2失败,跳过此步骤
>在此迭代中返回c(x2),即c((3,[“abc”])).

c只是返回一个有效的MatchResult,因为我们在模式的末尾.这意味着d1返回此结果,并且第一次迭代返回从步骤10传递它.

基本上,正如您所看到的,导致JS行为与PCRE不同的规范行如下:

a. If min is zero and y‘s endIndex is equal to x‘s endIndex,return failure.

结合使用时:

  1. Call c(x) and return its result.

如果迭代失败,则返回先前捕获的值.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读