源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

正则基础之 NFA引擎匹配原理

  • 时间:2022-01-19 08:51 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:正则基础之 NFA引擎匹配原理

1       为什么要了解引擎匹配原理

一个个音符杂乱无章的组合在一起,弹奏出的或许就是噪音,同样的音符经过作曲家的手,就可以谱出非常动听的乐曲,一个演奏者同样可以照着乐谱奏出动听的乐曲,但他/她或许不知道该如何去改变音符的组合,使得乐曲更动听。 作为正则的使用者也一样,不懂正则引擎原理的情况下,同样可以写出满足需求的正则,但是不知道原理,却很难写出高效且没有隐患的正则。所以对于经常使用正则,或是有兴趣深入学习正则的人,还是有必要了解一下正则引擎的匹配原理的。

2       正则表达式引擎

正则引擎大体上可分为不同的两类:DFA和NFA,而NFA又基本上可以分为传统型NFA和POSIX NFA。 DFA Deterministic finite automaton 确定型有穷自动机 NFA Non-deterministic finite automaton 非确定型有穷自动机 Traditional NFA POSIX NFA DFA引擎因为不需要回溯,所以匹配快速,但不支持捕获组,所以也就不支持反向引用和$number这种引用方式,目前使用DFA引擎的语言和工具主要有awk、egrep 和 lex。 POSIX NFA主要指符合POSIX标准的NFA引擎,它的特点主要是提供longest-leftmost匹配,也就是在找到最左侧最长匹配之前,它将继续回溯。同DFA一样,非贪婪模式或者说忽略优先量词对于POSIX NFA同样是没有意义的。 大多数语言和工具使用的是传统型的NFA引擎,它有一些DFA不支持的特性:   捕获组、反向引用和$number引用方式;   环视(Lookaround,(?<=…)、(?<!…)、(?=…)、(?!…)),或者有的有文章叫做预搜索;   忽略优化量词(??、*?、+?、{m,n}?、{m,}?),或者有的文章叫做非贪婪模式;   占有优先量词(?+、*+、++、{m,n}+、{m,}+,目前仅Java和PCRE支持),固化分组(?>…)。 引擎间的区别不是本文的重点,仅做简要的介绍,有兴趣的可参考相关文献。

3       预备知识

[h1]3.1     字符串组成[/h1] [img]http://files.jb51.net/upload/20090724155837175.jpg[/img] 对于字符串“[b]abc[/b]”而言,包括三个字符和四个位置。 [h1]3.2     占有字符和零宽度[/h1] 正则表达式匹配过程中,如果子表达式匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。 占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。 [h1]3.3     控制权和传动[/h1] 正则的匹配过程,通常情况下都是由一个子表达式(可能为一个普通字符、元字符或元字符序列组成)取得控制权,从字符串的某一位置开始尝试匹配,一个子表达式开始尝试匹配的位置,是从前一子表达匹配成功的结束位置开始的。如正则表达式: [b]([/b][b]子表达式一)(子表达式二)[/b] 假设[b](子表达式一)[/b]为零宽度表达式,由于它匹配开始和结束的位置是同一个,如位置0,那么[b](子表达式二)[/b]是从位置0开始尝试匹配的。 假设[b](子表达式一)[/b]为占有字符的表达式,由于它匹配开始和结束的位置不是同一个,如匹配成功开始于位置0,结束于位置2,那么[b](子表达式二)[/b]是从位置2开始尝试匹配的。 而对于整个表达式来说,通常是由字符串位置0开始尝试匹配的。如果在位置0开始的尝试,匹配到字符串某一位置时整个表达式匹配失败,那么引擎会使正则向前传动,整个表达式从位置1开始重新尝试匹配,依此类推,直到报告匹配成功或尝试到最后一个位置后报告匹配失败。

4       正则表达式简单匹本过程

[h1]4.1     基础匹配过程[/h1]   [img]http://files.jb51.net/upload/20090724155839624.jpg[/img] 源字符串:[b]abc[/b] 正则表达式:[b]abc[/b] 匹配过程: 首先由字符“[b]a[/b]”取得控制权,从位置0开始匹配,由“[b]a[/b]”来匹配“[b]a[/b]”,匹配成功,控制权交给字符“[b]b[/b]”;由于“[b]a[/b]”已被“[b]a[/b]”匹配,所以“[b]b[/b]”从位置1开始尝试匹配,由“[b]b[/b]”来匹配“[b]b[/b]”,匹配成功,控制权交给“[b]c[/b]”;由“[b]c[/b]”来匹配“[b]c[/b]”,匹配成功。 此时正则表达式匹配完成,报告匹配成功。匹配结果为“[b]abc[/b]”,开始位置为0,结束位置为3。   [h1]4.2     含有匹配优先量词的匹配过程——匹配成功(一)[/h1] [img]http://files.jb51.net/upload/20090724155839877.jpg[/img] 源字符串:[b]abc[/b] 正则表达式:[b]ab?c[/b] 量词“[b]?[/b]”属于匹配优先量词,在可匹配可不匹配时,会先选择尝试匹配,只有这种选择会使整个表达式无法匹配成功时,才会尝试让出匹配到的内容。这里的量词“[b]?[/b]”是用来修饰字符“[b]b[/b]”的,所以“[b]b?[/b]”是一个整体。 匹配过程: 首先由字符“[b]a[/b]”取得控制权,从位置0开始匹配,由“[b]a[/b]”来匹配“[b]a[/b]”,匹配成功,控制权交给字符“[b]b?[/b]”;由于“[b]?[/b]”是匹配优先量词,所以会先尝试进行匹配,由“[b]b?[/b]”来匹配“[b]b[/b]”,匹配成功,控制权交给“[b]c[/b]”,同时记录一个备选状态;由“[b]c[/b]”来匹配“[b]c[/b]”,匹配成功。记录的备选状态丢弃。 此时正则表达式匹配完成,报告匹配成功。匹配结果为“[b]abc[/b]”,开始位置为0,结束位置为3。 [h1]4.3     含有匹配优先量词的匹配过程——匹配成功(二)[/h1] [img]http://files.jb51.net/upload/20090724155839784.jpg[/img] 源字符串:[b]ac[/b] 正则表达式:[b]ab?c[/b] 匹配过程: 首先由字符“[b]a[/b]”取得控制权,从位置0开始匹配,由“[b]a[/b]”来匹配“[b]a[/b]”,匹配成功,控制权交给字符“[b]b?[/b]”;先尝试进行匹配,由“[b]b?[/b]”来匹配“[b]c[/b]”,同时记录一个备选状态,匹配失败,此时进行回溯,找到备选状态,“[b]b?[/b]”忽略匹配,让出控制权,把控制权交给“[b]c[/b]”;由“[b]c[/b]”来匹配“[b]c[/b]”,匹配成功。 此时正则表达式匹配完成,报告匹配成功。匹配结果为“[b]ac[/b]”,开始位置为0,结束位置为2。其中“[b]b?[/b]”不匹配任何内容。 [h1]4.4     含有匹配优先量词的匹配过程——匹配失败[/h1] [img]http://files.jb51.net/upload/20090724155839224.jpg[/img] 源字符串:[b]abd[/b] 正则表达式:[b]ab?c[/b] 匹配过程: 首先由字符“[b]a[/b]”取得控制权,从位置0开始匹配,由“[b]a[/b]”来匹配“[b]a[/b]”,匹配成功,控制权交给字符“[b]b?[/b]”;先尝试进行匹配,由“[b]b?[/b]”来匹配“[b]b[/b]”,同时记录一个备选状态,匹配成功,控制权交给“[b]c[/b]”;由“[b]c[/b]”来匹配“[b]d[/b]”,匹配失败,此时进行回溯,找到记录的备选状态,“[b]b?[/b]”忽略匹配,即“[b]b?[/b]”不匹配“[b]b[/b]”,让出控制权,把控制权交给“[b]c[/b]”;由“[b]c[/b]”来匹配“[b]b[/b]”,匹配失败。此时第一轮匹配尝试失败。 正则引擎使正则向前传动,由位置1开始尝试匹配,由“[b]a[/b]”来匹配“[b]b[/b]”,匹配失败,没有备选状态,第二轮匹配尝试失败。 继续向前传动,直到在位置3尝试匹配失败,匹配结束。此时报告整个表达式匹配失败。 [h1]4.5     含有忽略优先量词的匹配过程——匹配成功[/h1] [img]http://files.jb51.net/upload/20090724155839505.jpg[/img] 源字符串:[b]abc[/b] 正则表达式:[b]ab??c[/b] 量词“[b]??[/b]”属于忽略优先量词,在可匹配可不匹配时,会先选择不匹配,只有这种选择会使整个表达式无法匹配成功时,才会尝试进行匹配。这里的量词“[b]??[/b]”是用来修饰字符“[b]b[/b]”的,所以“[b]b??[/b]”是一个整体。 匹配过程: 首先由字符“[b]a[/b]”取得控制权,从位置0开始匹配,由“[b]a[/b]”来匹配“[b]a[/b]”,匹配成功,控制权交给字符“[b]b??[/b]”;先尝试忽略匹配,即“[b]b??[/b]”不进行匹配,同时记录一个备选状态,控制权交给“[b]c[/b]”;由“[b]c[/b]”来匹配“[b]b[/b]”,匹配失败,此时进行回溯,找到记录的备选状态,“[b]b??[/b]”尝试匹配,即“[b]b??[/b]”来匹配“[b]b[/b]”,匹配成功,把控制权交给“[b]c[/b]”;由“[b]c[/b]”来匹配“[b]c[/b]”,匹配成功。 此时正则表达式匹配完成,报告匹配成功。匹配结果为“[b]abc[/b]”,开始位置为0,结束位置为3。其中“[b]b??[/b]”匹配字符“[b]b[/b]”。 [h1]4.6     零宽度匹配过程[/h1] [img]http://files.jb51.net/upload/20090724155839409.jpg[/img] 源字符串:[b]a12[/b] 正则表达式:[b]^[/b][b](?=[a-z])[/b][b][a-z0-9]+[/b][b]$ [/b] 元字符“[b]^[/b]”和“[b]$[/b]”匹配的只是位置,顺序环视“[b](?=[a-z])[/b]”只进行匹配,并不占有字符,也不将匹配的内容保存到最终的匹配结果,所以都是零宽度的。 这个正则的意义就是匹配由字母和数字组成的,第一个字符是字母的字符串。 匹配过程: 首先由元字符“[b]^[/b]”取得控制权,从位置0开始匹配,“[b]^[/b]”匹配的就是开始位置“[b]位置0[/b]”,匹配成功,控制权交给顺序环视“[b](?=[a-z])[/b]”; “[b](?=[a-z])[/b]”要求它所在位置右侧必须是字母才能匹配成功,零宽度的子表达式之间是不互斥的,即同一个位置可以同时由多个零宽度子表达式匹配,所以它也是从位置0尝试进行匹配,位置0的右侧是字符“[b]a[/b]”,符合要求,匹配成功,控制权交给“[b][a-z0-9]+[/b]”; 因为“[b](?=[a-z])[/b]”只进行匹配,并不将匹配到的内容保存到最后结果,并且“[b](?=[a-z])[/b]”匹配成功的位置是位置0,所以“[b][a-z0-9]+[/b]”也是从位置0开始尝试匹配的,“[b][a-z0-9]+[/b]”首先尝试匹配“[b]a[/b]”,匹配成功,继续尝试匹配,可以成功匹配接下来的“[b]1[/b]”和“[b]2[/b]”,此时已经匹配到位置3,位置3的右侧已没有字符,这时会把控制权交给“[b]$[/b]”; 元字符“[b]$[/b]”从位置3开始尝试匹配,它匹配的是结束位置,也就是“[b]位置3[/b]”,匹配成功。 此时正则表达式匹配完成,报告匹配成功。匹配结果为“[b]a12[/b]”,开始位置为0,结束位置为3。其中“[b]^[/b]”匹配位置0,“[b](?=[a-z])[/b]”匹配位置0,“[b][a-z0-9]+[/b]”匹配字符串“[b]a12[/b]”,“[b]$[/b]”匹配位置3。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部