正则表达式贪婪模式、懒惰模式与独占模式浅析

正则表达式贪婪模式、懒惰模式与独占模式浅析

一、正则表达式引擎:

      正则表达式的执行,是由正则表达式引擎编译执行的,正则表达式引擎分为DFA(Deterministic finite automaton,确定型有穷自动机)和NFA(Non-deterministic finite automaton,非确定型有穷自动机)两类,确定型即在没有正则表达式时就可以确定的按照文本顺序直接确定匹配的顺序,非确定型的文本匹配顺序则与所编写的正则表达式有关,因此,我们可以认为DFA是由文本主导的,而NFA是由正则表达式主导的。
      在使用DFA时,引擎会按照从左到右的顺序直接进行匹配,也就是说每处的匹配只会进行一次,在这种情况下,执行速度会更快,但是它的缺点也很明显,支持的特性较少,无法支持捕获组,反向引用等功能,而且在比较诸如(a|b|c|d|…)会同时将这些情况同时拿出来比较,十分占用资源,如果表达式写的不合理,会造成机器崩溃等后果。
      在使用NFA时,引擎会根据正则表达式来匹配文本,它会不断读入字符,尝试是否符合当前正则表达式,如果不符合,会吐出字符重新尝试,通常情况下会慢于DFA,但是NFA能支持DFA不能支持的一些非常方便的特性,如上面提到的捕获组和反向引用等。因此,为了编程方便,在实际学习工作中应用更多。
      通过匹配原理我们可以看出DFA是以功能和资源占用为代价换取了匹配速度,而NFA正好与之相反。由于上面只是讲了原理不够直观,下面具体举例进行说明。

String text = "hi Tom,hello"
String regex = "he(l|la|lb|lc|ll)o"

      在DFA时,引擎会先寻找第一个h,然后寻找h后面的e,发现第一个h处匹配失败,直接放弃,去寻找第二个h,再寻找后面的e,发现可以匹配,遇到(l|la|lb|lc|ll) 后开始同时进行几个条件的匹配,即并行匹配,如果几个条件均不符合则放弃此处继续向后匹配,如果找到合适的,就会继续匹配表达式的剩余条件,由于不走回头路的特性,其匹配速度一般比较快,但是并行匹配比较消耗资源,对机器性能要求高。
      在NFA时,主要区别在于对于(l|la|lb|lc|ll)这种可选条件的处理,如果第一个选项失败,则会回退到可选条件之前,重新对下一个选项进行匹配,直到找到匹配的选项,然后继续匹配选项后面表达式剩余的条件;或发现所有的选项都不匹配,然后放弃此处。
      再次强调,DFA下,任何效果相同的正则表达式匹配过程均相同;NFA下,效果相同的不同正则表达式其匹配过程可能不同。

二、NFA的回溯机制:

      回溯,即在匹配过程中由当前位置吐出若干个字符再与正则表达式后面的条件重新进行匹配。
      举个比较经典的例子:

String regex = "ab{1,3}c"

      当要匹配的字符串为abbbc时,a匹配完成后,b匹配三次,然后c进行匹配,匹配完成,整个过程中不会发生回溯。
      当要匹配的文本为’"abc"时,1~2步应该都好理解,但是为什么在第3步开始,虽然已经文本中已经有一个b匹配了b{1,3},后面还会拉着字母c跟b{1,3}做比较呢?这个就是我们下面将要提到的正则的贪婪特性,也就是说b{1,3}会竭尽所能的匹配最多的字符。在这个地方我们先知道它一直要匹配到撞上南墙为止。 在这种情况下,第3步发生不匹配之后,整个匹配流程并没有走完,而是像栈一样,将字符c吐出来,然后去用正则表达式中的c去和文本中的c进行匹配。这样就发生了一次回溯。

在这里插入图片描述

三、正则表达式的三种模式:

      正则表达式一共有三种模式:贪婪模式、懒惰模式和独占模式。下面分别对三种模式进行简单讨论。
      在讨论几个模式前要先讲一下可能涉及到的几个常见正则表达式元字符(此名称来自百度百科):
i. ?:匹配前面的子表达式零次或一次;
ii.+:匹配前面的子表达式一次或多次(大于等于1次)。
iii.*:匹配前面的子表达式任意次。
iv.{n,m}:即{min,max},最少匹配n次,最多匹配m次。当m省略时,则代表最少匹配n次,最多匹配次数无上限。
a.贪婪模式:默认情况下,正则表达式的匹配都会采用贪婪模式,贪婪模式下,会尽可能多的匹配符合正则表达式的内容。
b.懒惰模式:也称非贪婪模式,懒惰模式尽可能少地匹配所搜索的字符串,开启方法为正则表达式该部分后加上?。
如果上面表达式改为:

String regex = "ab{1,3}?c"

则在匹配’abc’和’abbc’时均不会发生回溯,"abc"匹配过程如下:
在这里插入图片描述
c.独占模式:可以理解为不会回溯的贪婪模式,但是如’abc’和’abbc’用下面的表达式会出现不匹配的情况,所以建议使用的时候要慎重,除非条件比较苛刻或者对要匹配的数据或文本十分了解,但是由于这种模式下不会发生回溯,所以资源消耗较小。

// 独占模式
String regex = "ab{1,3}+c"

"abbc"用上式的匹配过程如下图,结果是匹配不超过,去掉+即关闭独占模式后匹配成功。
在这里插入图片描述
下面附三种模式下常用表达式形式:

贪婪懒惰独占
x?x?x?+
x*x*?x*+
x+x+?x+
x{n}x{n}?x{n}+
x{n,}x{n,}?x{n,}+
x{n,m}x{n,m}?x{n,m}+
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值