正则表达式

Posted 编程101

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正则表达式相关的知识,希望对你有一定的参考价值。




小编說

大家好,这是我们这个系列的最后一章,让我们开始吧!




大家好,在这一章中,我们接着上一章,继续来讲正则表达式的引擎。上一章中我们提到了DFA引擎中使用的目标串导向匹配,我们下面来讲NFA引擎中使用的模式导向匹配。


模式导向:


与目标串导向中一样,模式导向中,引擎也会花费一定的时间来预先编译模式,但是花费的时间比目标串导向花费的时间要少,而且得到的结果也和目标串导向引擎编译得到的结果一样,模式导向下的引擎编译仅仅得到了一个迷你程序,供之后的匹配和可能的回溯使用。


之后,在尝试匹配某个目标串的时候,引擎首先查看模式串的第一个元字符,如果匹配目标串的第一个字符,那么就进行下一个字符的匹配;如果不匹配,则从目标串的第二个字符开始,来匹配模式串的第一个元字符。与目标串导向匹配不同,在目标串导向匹配中,主角是目标串,通过移动目标串字符,看到是否在模式串中存在可能的匹配;而在这里,主角是模式串,通过一个接一个地比较元字符与目标串字符,确定是否匹配成功。所以这也是为什么一个叫模式串导向而另一个叫目标串导向。


那么这个时候可能就有同学有疑问了,如果我们是以模式串为导向,那么遇到在模式串中出现了’*’,’+’,’?’,’{min,max}’等元字符怎么办?这个时候它们是匹配一个目标串字符,还是两个还是多个呢?这是一个好问题,这就是我们马上会涉及到的,模式导向里面的回溯问题。


回溯,简单来说,就是在模式串导向匹配的时候,遇到有贪婪匹配属性的元字符——等等,你说你忘记了什么是贪婪匹配?别急,先回去看看之前的章节——我们先让它充分的发挥‘吃货’的属性,尽可能的多匹配目标串字符,只不过,在每次多吃入一个目标串字符的时候,引擎会建立一个回溯点,也可以理解为存档,以便之后在有需要的时候元字符把吃进去的目标串字符吐出来——吃了吐,就是这么个意思。


我们还是来看个例子吧。用’H(.*)lo’来匹配’Hello‘。


刚刚开始的时候,一切都很平常,首字符’H’匹配成功,开始比较第二个元字符。

这个时候,好玩儿的事情发生了,因为(.*)明显具有吃货属性,所以它吃完了所有的目标串字符,只不过,每吃下一个目标串字符的时候,有个回溯点被建立。

正则表达式(五)

这儿建立了四个回溯点,因为(.*)已经吃完了所有的目标串字符,所以这个吃货也被满足了,引擎接着往下匹配剩余的模式。接着下来是一个’l’,因为这个时候目标串已经什么都不剩了,所以’l’没办法匹配,匹配失败。那么这个时候是否引擎就宣布该模式串匹配不了目标串呢?其实也不是,因为还有回溯点,还有存档嘛,大不了引擎就读档再试试呗,记住,模式导向的匹配,只有在所有读档都尝试过,并且都失败的情况下,引擎才会宣布整个匹配失败。让我们读档试试,第一个档,即回溯点在’o’这里。

正则表达式(五)

明显,’l’和’o’匹配不上,我们继续读下一个档。

正则表达式(五)

这次看来似乎可行了,’l’’l’匹配,但是很不幸,下一个模式串字符是’l’而下一个目标串字符是’o’,这个匹配也失败了,继续读下一个档。

完美!这次剩余的模式串字符和目标串字符完美匹配了,这时引擎宣布匹配成功!从这个例子大家可以看到回溯的工作机制。同时也希望大家明白,为什么目标串导向匹配的DFA工作效率一般比模式导向匹配的NFA要高,因为DFA没有回溯机制。不会频繁的读档再尝试。


回溯不仅会发生在贪婪匹配属性的元字符中,也会发生在选择匹配符中。还记得上一章讲过的判断NFA还是DFA引擎的办法吗?用(Hello|Hello World)选择匹配符匹配目标串’Hello World’。因为DFA是目标串导向,总是能返回最长的匹配,所以DFA会匹配整个目标串,这些我们已经讲过了。我们现在把这个例子的剩下一半讲完,说说为什么NFA会返回Hello部分。


在匹配开始的时候,遇到了选择匹配符,引擎从第一个选择开始尝试匹配,同时在其他选择分支建立回溯点,如果第一个选择成功匹配,就选择它不再尝试其他回溯点。

所以我们可知,这个匹配仅仅会匹配目标串中的’Hello’部分。反之如果选择匹配符是(HelloWorld|Hello),那么整个目标串都会被匹配。在模式导向中,选择匹配符有First Come, First Serve的性质,我们在使用这种引擎的时候一定要注意。


结语:



这是小编第一次写技术性短文,文笔颇有欠缺,所有坚持看到最后的同学,小编对你们说声感谢,敬请期待下一个系列!


以上是关于正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

PHP 正则表达式总结

正则表达式

正则表达式

正则表达式“或“的使用

正则表达式 验证数字格式 非负数 小数点后保留两位 数字正则 double正则

JS正则表达式详解