贪婪匹配、非贪婪匹配、占有优先匹配

在基础正则中,那些能匹配多次的量词默认都会匹配最长内容。这种尽量多匹配的行为称为【贪婪匹配】(greedy match)。

例如:

"aa1122ccbb" =~ /a.*c/;

上面正则中的.*将直接从第二个字母a开始匹配到最结尾的b,因为从第二个字母a开始到最后一个字母b都符合.*的匹配模式。再然后,去匹配字母c,但因为已经把所有字母匹配完了,只能一个字符一个字符地回退释放,每释放一个字符就匹配一次字母c,发现回退释放到倒数第三个字符c就能满足匹配要求,于是这里的.*最终匹配的内容是a1122c

上面涉及到回溯的概念,表示将那些已经被匹配的内容回退释放。

上面描述的是贪婪匹配行为,还有非贪婪匹配、占有优先匹配。简单描述下:

  • 非贪婪匹配:(lazy match)尽可能少地匹配,也叫做懒惰匹配
  • 占有优先匹配:(possessive)只要占有就不再交还回溯

有必要搞清楚这几种匹配模式在匹配机制上的区别:

  • 贪婪匹配:对于那些量词,将一次性从左到右匹配到最大长度,然后再往回回溯释放
  • 非贪婪匹配:对于那些量词,将从左向右逐字符匹配最短长度,然后直接结束这次的量词匹配行为
  • 占有优先匹配:先按照贪婪模式匹配,匹配后就【锁】住已匹配内容,不允许进行回溯

贪婪匹配模式、非贪婪匹配模式和占有优先匹配模式对应的量词元字符如下:

                  (量词后加上?)         (量词后加上+)
  贪婪匹配量词    非贪婪匹配量词       占有优先匹配量词
------------------------------------------------------
      *              *?                    *+
      ?              ??                    ?+
      +              +?                    ++
      {M,}           {M,}?                 {M,}+
      {M,N}          {M,N}?                {M,N}+
      {N}            {N}?                  {N}+

几点需要说明:

  • 非贪婪匹配时,{M,}?{M,N}?等价,因为最多只匹配M次
  • Perl不支持{,N}的量词模式,所以也没有对应的非贪婪和占有优先匹配模式
  • 由于{N}是精确匹配N次的量词,所以贪婪与否对最终结果无关紧要,但是却影响匹配时的行为:贪婪匹配最长,需要回溯,非贪婪匹配最短,不回溯,占有优先匹配最长不回溯

看以下示例即可理解贪婪和非贪婪匹配的行为:

my $str="abc123abc1234";

# greedy match
if( $str =~ /(a\w*3)/){
  say "$&";       # abc123abc123
}

# lazy match
if( $str =~ /(a\w*?3)/){
  say "$&";      # abc123
}

占有优先匹配模式示例:

my $str="abc123abc1234";

if( $str =~ /a\w*+/){     # 成功
  say "possessive1: $&";
}
if( $str =~ /a\w*+3/){    # 失败
  say "possesive2: $&";
}

从上面第二个示例可知,使用占有优先匹配模式时,它后面不应该跟其他正则表达式,例如a*+x永远匹配不了东西。