可以在AC算法上扩展的正则特性包括:“.*”,“.”,“^”,“$”等。
AC算法的查找流程:
AC算法的查找流程:
再经过一系列有些绕的过程之后(不是扩展的重点,跳过),整个ACSM_STRUCT结构已经建立完毕。开始查找。查找伪代码:
总结一下:
1、 从状态0开始进行状态跳转。
2、 状态跳转表在search操作之前已经建立好了,在状态N下,输入字符c,跳转到状态M,按照状态跳转表来就好了。
3、 状态机到达哪些状态时发生匹配,匹配了哪些pattern,这也是之前就算出来了的。
4、 整个search操作,就是把search的buf作为输入,在预先设计好的状态机上进行跳转,每到一个新的状态,就看一下在这个状态下有没有发生匹配,如果有就打印,如果没有就继续,直到search到buf末尾。
对“.*”的扩展:
基本思路是把包含了.*等正则特性正则表达式(ab.*cd)分成多个固定串(ab和cd),当固定串按照顺序或者位置依次发生匹配时,则认为整个正则表达式发生匹配了。这里的ab和cd在查找过程中,是按照两个独立的字符串进行查找的,但是和完全独立的两个字符串不同,ab和cd又同属于一个正则表达式ab.*cd,应该具备某种特定的联系。
这里使用pattern的iid来表示这种联系。首先ab和cd各有一个对应的ACSM_PATTERN结构,分别进行匹配。ab的iid是0x21??;cd的iid是0x22??,这里要求iid的低16bit ab和cd相同,而又和其他的pattern区分开。而iid的16~23bit表示ab、cd在正则表达式中出现的顺序(ab是1,cd是2),iid的24~31bit(0x2)表示正则表达式被拆分成了几个固定串。
在匹配过程中要记录之前发生匹配的pattern的iid,如果低16bit相同并且高16bit不为0,则要求pattern iid的16~23bit要严格递增,否则就是一个无效的匹配。当到达buf末尾时,比较pattern iid的16~23bit和24~31bit,只有两者相等的时候才认为整个正则表达式(ab.*cd)发生匹配了。
这里还有个问题,比如正则表达式abc.*bcd,而buf=”abcd”,使用上述方法你会惊奇的发现匹配发生了,而实际上没有发生匹配,原因是”abcd”可以分别匹配”abc”和”bcd”,并且先匹配”abc”,后匹配”bcd”,但是匹配的位置发生了重叠。所以还需要引入一个数组,记录匹配发生的结束位置。如abc发生匹配时,记录匹配发生的结束位置是3,而bcd发生匹配时,发现bcd发生匹配的起始位置是2,发生了重叠,也认为这是一个无效匹配。
此方法的局限性显而易见,pattern的总数不能超过2^16,每一个正则表达式被拆分之后的子pattern数不能超过2^8。
对“.”的扩展:
和“.*”的扩展方法类似,如正则表达式(ab…cd),被拆分成固定串ab和cd,但是需要在ACSM_PATTERN中增加成员变量,记录各个子pattern之间的相对位置。而在search时,只有严格按照相对位置发生的匹配才被认为是有效的匹配
对“^”和“$”的匹配:
其实也比较简单,就是在search时,判断一下匹配发生起始位置和结束位置是不是buf的开头和末尾就好了
上述扩展不会影响search的效率,还是O(n)级的。
对“+”,“?”,“*”,“{n,m}”的扩展:
变成O(nlogn)的了,还不如找个pcre、awk的源码看下了。