正则表达式之回溯

所属分类: 网络编程 / 正则表达式 阅读数: 597
收藏 0 赞 0 分享
关于“回溯”我也是第一次接触,对它也不算很了解。下面就把我所了解的做为一个心德记录下来,以备查看。

我们所使用的正则表达式的匹配基础大概分为:优先选择最左端(最靠开头)的匹配结果和标准的匹配量词(*、+、?和{m, n})是匹配优先的。

“优先选择最左端的匹配”顾名思义就是从字符串的起始位置开始匹配直到匹配结束这是基础;“标准匹配量词”又分为“非确定型有穷自动机(NFA)”也可以叫做“表达式主导”;另外一种是“确定型有穷自动机(DFA)”也可以叫做“文本主导”。我们目前在JavaScript中所使用的正则表达式为“表达式主导”。表达式主导和文本主导解释起来有些麻烦,先看来一个例子可能会清楚些。

复制代码 代码如下:

// 使用正则表达式匹配文本
var reg = /to(nite|knight|night)/;
var str = 'doing tonight';
reg.test(str);

在上面的这个例子中,第一个元素[t],它将会重复尝试,直到目标字符串中找到‘t'为止。之后,就检查紧随其后的字符是否能由[o]匹配,如果能,就检查下面的元素(nite|knight|night)。它的真正含义是“nite”或者“knight”或者“night”。引擎会依次尝试这3种可能。尝试[nite]的过程是先尝试[n],然后[i],然后[t],最后是[e]。如果这种尝试失败,引擎会尝试另一种可能,如此继续下去,直到匹配成功或是报告失败。表达式中的控制权在不同的元素之间转换,所以称为“表达式主导”。

    同样是上面的例子“文本主导”在扫描字符串时,会记录当前有效的所有匹配可。当引擎移动到t时,它会在当前处理的匹配可能中添加一个潜在的可能:

字符串中的位置 正则表达中的位置
……doing tonight 可能的匹配位置:/to(nite|knight|nigth)/

 

接下来扫描的每个字符,都会更新当前的可能匹配序列。继续扫描两个字符以后的情况是:

 

字符串中的位置 正则表达中的位置
……doing tonight 可能的匹配位置:/to(nite|knight|nigth)/

 

有效的可能匹配变为两个(knight被淘汰出局)。扫描到g时,就只剩下一个可能匹配了。当h和t匹配完成后,引擎发现匹配已经完成,报告成功。“文本主导”是因为它扫描的字符串中的每个字符都对引擎进行了控制。

    如果想要弄明白“表达式主导”是如何工作的,那就要看一下我们今天的主题“回溯(backtracking)”。回溯就像是在走岔路口,当遇到岔路的时候就先在每个路口做一个标记。如果走了死路,就可以照原路返回,直到遇见之前所做过的标记,标记着还未尝试过的道路。如果那条路也走不能,可以继续返回,找到下一个标记,如此重复,直到找到出路,或者直到完成所有没有尝试过的路。

    在许多情况下,正则引擎必须在两个(或更多)选项中做出选择。当遇到/……x?……/时,引擎必须是否尝试匹配X。对于/……X+……/的情况,毫无疑问,X至少尝试匹配一次——因为加号要求必须匹配至少一次。第一个X匹配之后,此要求已经满足,需要决定是否尝试下一个X。如果决定进行,还要决定是否匹配第三个X,第四个X,如此继续。每次选择,其实就是做一个标记,用于提示此处还有另一个可能的选择,保留起来以备用。在回溯的过程中要考虑两个要点:哪个分支应当首先选择?回溯的时候使用的是哪个(或者是哪些个)之前保存的分支?

    第一个问题是按下面这条重要原则来选择的:

        如果需要在“进行尝试”和“路过尝试”之间选择,对于匹配优先量词,引擎会优先选择“进行尝试”,而对于忽略优先量词,会选择“路过尝试”。

    第二个问题是按以下这条原则:

        距离当前最近储存的选项就是当本地失败强制回溯时返回的。使用的原则是LIFO(last in first out,后进先出)。

    我们先来看几个在道路中做标记的例子:

        1、未进行回溯的匹配

            用[ab?c]来匹配“abc”。[a]匹配之后,匹配的当前状态如下:

“abc” ab?c

            现在轮到[b?]了,正则引擎需要决定:是需要尝试[b]呢,还是跳过?因为[?]是匹配优先的,它会尝试匹配。但是,为了确保在这个尝试最终失败之后能够恢复,引擎会把:

“abc” ab?c
            添加到备用状态序列中。也就是说,稍后引擎可能从下面的位置继续匹配:从正则表达式中的[b?]之后,字符串的c之前(也就是说当前的位置)匹配。这实际上就是跳过[b]的匹配,而问题容许这样做。引擎做好标记后,就会继续向前检查[b]。在示例中,它能够匹配,所以新的当前状态变为:
“abc” ab?c

            最终的[c]也能成功匹配,所以整个匹配完成。备用状态不再需要了,所以不再保存它们。

        2、进行了回溯的匹配

            下面要匹配的文本是“ac”,在尝试[b]之前,一切都与之前的过程相同。显然,这次[b]无法匹配。也就是说,对[……?]进行尝试的路走不通了。因为有一个备用状态,这个“局部匹配失败”产工会导致整体匹配失败。引擎会进行回溯,也就是说,把“当前状态”切换为最近保存的状态。

“ac” ab?c

            在[b]尝试之前保存的尚未尝试的选项。这时候,[c]可以匹配c,所以整个匹配宣告完成。

        3、不成功的匹配

            现在要匹配的文本是“abx”。在尝试[b]以前,因为存在问号,保存了这个备用状态:

“abx” ab?c

            [b]能够匹配,但这条路往下却走不通了,因为[c]无法匹配x。于是引擎会回溯到之前的状态,“交还”b给[c]来匹配。显然,这次测试也失败了。如果还有其他保存的状态,回溯会继续进行,但是此时不存在其他状态,在字符串中当前位置开始的整个匹配也就宣告失败。

    目前对正则表达式的回溯只能理解这么多,以后我再慢慢补充吧!

更多精彩内容其他人还在看

深入分析正则表达式的子模式

在正则表达式中,可以使用“(”和“)”将模式中的子字符串括起来,以形成一个子模式。将子模式视为一个整体时,那么它就相当于一个单个字符。下面我们就来详细了解下子模式
收藏 0 赞 0 分享

可以少写1000行代码的正则表达式

正则表达式,一个十分古老而又强大的文本处理工具,仅仅用一段非常简短的表达式语句,便能够快速实现一个非常复杂的业务逻辑。知道这20个正则表达式,能让你少写1000行代码,想知道吗
收藏 0 赞 0 分享

20个正则表达式必知(能让你少写1,000行代码)

这篇文章主要介绍了20个正则表达式必知(能让你少写1,000行代码)的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

JS通过正则限制 input 输入框只能输入整数、小数(金额或者现金) 两位小数

这篇文章主要介绍了JS通过正则限制 input 输入框只能输入整数、小数(金额或者现金) 两位小数的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

java常用正则表达式

这篇文章主要介绍了java常用正则表达式,需要的朋友可以参考下
收藏 0 赞 0 分享

如何快速学习正则表达式

在日常工作中,我们经常写正则表达式,比如在表单中经常用来验证用户输入的格式是否正确。接下来通过本文给大家介绍如何快速学习正则表达式,感兴趣的小伙伴一起学习吧
收藏 0 赞 0 分享

运用正则表达式匹配所有表名

这篇文章主要介绍了运用正则表达式匹配所有表名 的相关资料,非常不错,具有参考借鉴价值,需要的朋友一起看看吧
收藏 0 赞 0 分享

浅析正则表达式-替换原则(.NET) 图文

最近经常用到替换的东西所以就出来整理下,这里要分享的是正则表达式里面的替换原则,首先要声明的是这里提及到的替换原则是.NET里面的正则表达式的替换原则
收藏 0 赞 0 分享

正则替换实现输入框只能有数字、中英文逗号

最近在开发过程中,需要一个输入框里面只能有数字与中英文逗号,因为是相关文章,其它的也不让出现,容易造成问题,编程容易把介绍复制到里面,所以想到了这个方法
收藏 0 赞 0 分享

自动检测数字替换非数字的正则表达式

这篇文章主要介绍了自动检测数字替换非数字的正则表达式 ,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多