正则表达式引擎实现体会

来源:互联网 发布:cv source数据库 复旦 编辑:程序博客网 时间:2024/06/07 00:20

学编译原理的词法分析部分,看到了正则表达式,于是就动手自己实现了一番。
代码:https://github.com/earayu/Regex.git

正则表达式的解析:
正则表达式->NFA->DFA->模拟DFA运行的形式
除了这种方式以外,还能直接模拟NFA,虚拟机等方法,但还是DFA形式最接近编译原理。

一开始的时候并不知道如何下手,龙书中只是提供了几个算法(子集构造法等)和NFA、DFA的定义。并没有“手把手”地教如何实现正则表达式解析引擎。

我先是构建了NFA的数据结构—-有向图。DFA类似,然后实现了子集构造法。这样就可以把NFA转成DFA了,然后实现了DFA的模拟。但完全不知道如何从(带括号的)正则表达式转换为NFA。龙书中只是给了3个基本操作的转换,并没有太大帮助。后来才发现可以将正则表达式转换成后缀式,然后用双栈来构造NFA,这是关键的一步。

做完以上工作,终于可以完成 . | *3个操作了(以及括号功能)。这时候,我们再加入转义功能以及一些语法糖。
比如:[a-zA-Z0-9] + ? {m,n} \s \w 等

再深入的话就是加入一些高级功能:
(?=XXX) (?<=XXX) 等
这样DFA就不够用了,需要NFA。

关于正则引擎,需要2种功能:match和search。
match是先有一个正则表达式,如(a|b)*ccd。再用某个字符串去匹配。这个例子中 “abbaabaccd”匹配成功, “abbccc”匹配失败。这个实现很简单。

search就是查找,在字符串txt中寻找跟正则表达式匹配的子字符串,返回索引值。我不知道正轨套路是什么样的,但我根据KMP算法的思想,自己写了一个search方法。时间复杂度也是O(N)的,所以还算可以。

差不多写好了正则引擎,对长表达式测试了一下,发现构造DFA很慢。4000个字符的表达式花费了69秒,这显然是无法接受的。详细跟进代码,发现用子集构造法会给DFA加入很多无用的边,而每次子集的计算代价都是非常昂贵的。优化了一下算法之后相同表达式只花费了4秒,但仍然不够。算法的问题解决了,但代码仍然有问题。花了2个晚上之后,相同表达式运行时间为0.5秒。
这个性能我仍然不太满意,但由于一开始基于上手,构建的数据结构不太合理,加上大量运用了Java的标准库,继续优化下去已经很难了。要花费大量时间和精力,改动大量代码,那就这样吧。。。

过段时间,要是有空的话,可以尝试一下基于NFA的正则表达式,用C++重构,效率估计又能高很多。

0 0