前一段时间,在将 多正则表达式匹配工具 用于数十万任意的正则表达式时,之前一直担忧的问题终于出现了:NFA 转化 DFA 时的指数爆炸,那样的 DFA 根本建立不出来,由于那些正则表达式之间有不可预料的各类交集!正则表达式
这个问题对我打击很大,我甚至顿时以为 多正则表达式匹配工具 彻底是个废柴,最多,是个玩具!可是,只有挑战,才能激励人的斗志,挖掘人的潜能。我想起了曾经对之不屑一顾的动态 DFA 匹配算法,以前我在研究 RE2 时知道,RE2 在动态 DFA 的内存用量达到限定时,会抛弃已经建立整个动态 DFA,由于 DFA 的状态图比较复杂,节点之间互相引用,没法象普通的 Cache 同样部分的进行 Swap ,直觉上对它就没有好印象。算法
可是碰到组合爆炸这个魔咒,只有尝试一下,就先用 RE2 中的 set 试一下,先从那数十万正则表达式中任选一万个,re2 执行却是很快,但立马就提示 DFA Out Of Memory 了,而后我尝试将 re2 的内存设到 8G,仍是不行,浪费若干脑细胞,最后才惊奇地发现,Re2 的内存限制用的是 int 来表示的,而 int 的事实标准是32 bit,最大只能是 2G-1!设成 2G-1以后,re2 运行正常,性能还不错。工具
因而我开始加码,将全部正则表达式都扔进去,果真,re2 崩了!性能
虽然如此,可是至少代表,动态 DFA 解决该问题是可行的,我开始实现个人计划。一切都很顺利:测试
以前我实现其余算法时(r1303),扩展了自动机的字符集,而没有在自动机的状态上打专用 Tag,这个决定真是明智之举,在这里又用上了:google
ch=256 | 表示括号捕获 | 用于我最先实现 One Pass 正则表达式的 DFA 算法 |
ch=257 | 表示全匹配 | 以前在一遍扫描中捕获全部括号时,发现因括号位置太靠前而致使严重的性能问题, 就实现了两遍扫描捕获括号,第一遍只看全匹配,得到正则表达式ID, 第二遍专门针对具体正则ID捕获括号,性能要好的多 |
ch=258 | 表示伪Epsilon转移 | 这是实现动态 DFA 匹配的一个根基,不过这个伪 Epsilon 转移只起到一个链表的做用,将全部子 DFA 的根(初始状态)链起来 |
若是一切都照抄 re2 的实现,那不过是无聊的体力劳动。个人动态 DFA 实现,彻底创建在我以前的各类自动机算法之上,有了这个根基,个人实现相比 re2,有不少优点:spa