正则表达式

Posted 编程101

tags:

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




小编說

君子务本,本立而道生,让我们深入正则表达式的根本,看看它的引擎吧




        论语上说,“君子务本,本立而道生”,我们学习任何一门知识,语言甚至技能也是这样,只有在了解了隐藏在表面功能点下面的逻辑之后,我们才有可能真正掌握这门知识,从而灵活运用它。


        小编有个习惯,在学习任何一门编程语言的时候,如果不去看几本讲解技术内幕的大部头或者对于开源软件,不详细阅读一遍源代码,是万万不敢拍胸脯说真正掌握了的,虽然枯燥,虽然可能的预期回报很小,但这正是学习一项技能的必由之路。希望所有的读者都能戒骄戒躁,抱有刨根问底的精神来学习。


        有鉴于我们上几章已经学习了大部分的正则表达式语法,我们这章开始,进行深入一点的话题,聊聊正则表达式的引擎。对于正则表达式一知半解的读者或者暂时不想深入了解它的读者,可以先略过这章,因为它会比较枯燥。准备好了吗?开始了!


        相信大家都知道,正则表达式分为很多种流派,或者也叫实现。老一点的GREP, PERL, LEX, 年轻的JAVA,.NET等都对正则表达式有着强力支持,但可惜的是,不同语言实现之间,正则表达式的表现形式不尽相同。所以,在我们使用任何一个正则表达式实现的时候,有几个问题我们需要了解,比如,支持哪些元字符?支持哪些操作?当然,最重要的一点,是了解这个正则表达式是基于哪个引擎的?


        正则表达式有两种引擎,一种是DFA(Deterministic Finite Automaton),确定有限自动机;另一种是NFA(Nondeterministic Finite Automaton),非确定有限自动机。其实,还有第三种叫POSIX NFA,即满足POSIX(Portable Operation System Interface of UNIX)标准的NFA,不过POSIX NFA行为比较像DFA,所以我们为了简单起见,只考虑DFA和NFA两种引擎。这两种引擎在编译模式和使用模式进行目标串匹配的时候都表现出了不同的行为。


        DFA



  DFA 是目标串导向的引擎,至于目标串导向是什么意思,我们稍后再谈。同时DFA不支持捕获和后向引用,在awk, lex和egrep中比较常见,要想判断所使用的正则表达式引擎是不是DFA,有个小方法。使用模式(Hello|HelloWorld)去匹配串’Hello World’,如果匹配结果是HelloWorld,那么多半引擎就是DFA,这里只说多半是因为POSIX NFA也会得到相同的结果。反之,如果匹配结果是Hello,那么引擎就是肯定是NFA。


  NFA


  NFA是模式导向的引擎,支持捕获和后向引用。现在NFA使用频率比DFA更加频繁,像JAVA,.NET,包括NOTEPAD++等都是NFA引擎。


  可能有同学会觉得,似乎除了对后向引用与捕获的支持之外,这两种引擎的差别不是特别大。嗯,有这种想法是正确的,因为在小编看来,它们之间最大的区别在于匹配的方式不同,即我们之前说的,目标串导向和模式导向的不同,这极大的影响了它们的匹配效率和结果,走你!


        目标串导向


        在这种匹配方式下,引擎会花费一定的时间来编译模式,并使用内存来存储编译后的结果,至于具体那些结果是什么,我们也不用太纠结,我们只要了解到,引擎花费了时间和空间来充分认识模式,为之后的匹配做准备工作。


        之后,在尝试匹配某个目标串的时候,引擎从目标串的第一个字符出发,看是否在模式中存在可能的匹配,如果存在,则引擎开始匹配目标的第二个字符;如果在移动过程中,发现模式中不存在可能的匹配,则表示从目标串的这个字符开始,匹配是失败的,引擎会移动目标串,从第二个字符出发,再次重复刚才的匹配动作。如果在引擎移动到目标串的结尾之前,依然没有找到成功的匹配,则认为整个串匹配失败。让我们看一个例子吧。使用(today|tomorrow|holiday)来尝试匹配目标串’tonigh today’。


        引擎开始工作的时候,从目标串第一个字符开始。



        这个时候发现匹配串中有两个可能的匹配,因此引擎开始匹配第二个字符,直到匹配成功或者没有可能的匹配为止。当匹配到第三个字符的时候,发现匹配串中已经没有任何可能的匹配,因为第三个字符是’n’,这个时候,以目标串第一个字符开头的匹配宣告失败,引擎开始移动字符串,进行下一轮的匹配。


正则表达式(四)


当然也失败了,因为字符’o’在匹配串中不存在任何可能的匹配,引擎接着移动目标串。直到下一个’t’。


正则表达式(四)


这时,字符’t’存在可能的匹配,引擎接着匹配下一个字符’ ‘,可惜,这个字符不存在任何可能的匹配,引擎接着移动目标串。


从这个’t’开始,直到目标串结尾,所有字符都存在着可能的匹配,所以目标串是一个成功的匹配,可以匹配的部分就是后面的’today’。当然,如果匹配的目标串是’tonighttodo’ 而不是’tonight today’,那么这个匹配就会失败。


除此之外,目标串导向的匹配还有一个可爱的特征,它总是会返回最长的匹配。还记得刚刚开始的(Hello|Hello World)匹配’HelloWorld’的例子吗?对于目标串导向的匹配来说,因为它关注的对象是目标串,所以在’Hello’子串已经匹配成功的情况下,仍然会尝试匹配下一个目标串字符。



所以这是为什么这个表达式可以匹配整个目标串的原因,也是为什么这个表达式可以用来判断是否是DFA的原因。


本来想一口气把NFA的模式导向也写在这一章,但NFA的模式导向涉及内容还是很多,包括回溯和优化的一些话题,所以这章暂时就到这里吧,下章我们继续聊模式导向的匹配,希望大家喜欢这章的内容!

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

PHP 正则表达式总结

正则表达式

正则表达式

正则表达式“或“的使用

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

JS正则表达式详解