简易正则表达式引擎的实现

正则表达式基本每个程序员都会用到,实现正则表达式引擎却似乎是一个很难的任务。实际上,掌握《编译原理》前端的词法分析部分知识就能够实现一个简单的正则表达式引擎。这里推荐一下网易云课堂的课程。http://mooc.study.163.com/course/USTC-1000002001?tid=1000003000#/info

基本的正则表达式  正则表达式由字符与元字符组成,整个表达式用于描述符合某些特定特征的一类字符串,比如说表达式:abc,它表示 "abc" 这个字符串,由 ‘a‘, ‘b‘, ‘c‘ 三个字符按顺序连接在一起。本文要实现的正则表达式比较简单,只实现连接、选择、闭包的功能。定义直接从ppt里截图:

实现的大概步骤如下:

NFA指的是非确定自动机,对任意的字符,有多个状态可以转换。DFA指的是确定自动机,对任意的字符,最多只有一个状态可以转换。

Thompson算法是一个递归算法,先将单个字符转换为nfa,再根据规则将nfa组合起来。单个字符(比如c)的转化如下

两个字符(比如e1e2)则中间用ε连接

接下来是选择(比如e1|e2)

闭包(比如e1*)比较复杂

知道了如何将小的nfa组合成大的nfa,那怎么处理正则表达式,将其转换为nfa呢?我们可以像处理四则运算那样,用两个栈来解决,但这只能应对简单情况。还可以用递归下降分析构建抽象语法树,a|b的语法树如下,递归下降分析的具体做法可以看前面推荐的视频或直接看源码

由于nfa对任意的字符,有多个状态可以转换,我们需要将其转换为dfa。我们的nfa实际上是ε-NFA,有很多ε边,而dfa是没有ε边的,所以我们可以通过子集构造算法去除这些ε边。子集构造算法的大概思路是将从状态A出发接收某个字符能到达的所有状态(包括接收字符后再通过ε边到达)构成一个子集,这个子集所能到达的所有状态又构成一个子集,到最后就能消除ε边,得到dfa。

对于下图的nfa

子集构造算法步骤如下

第一列第一行I的意思是从NFA的起始节点经过任意个ε所能到达的结点集合。Ia表示从该集合开始接收一个a所能到达的集合,Ib也就是接收一个b所能到达的状态的集合。

如果Ia和Ib还没出现在I,就把它们填在接下的I里。结果如下

得到dfa后再用Hopcroft算法最小化dfa,这个算法的思想是将等价的状态浓缩为一个结点。比如对于以下dfa

可以简化为

这样我们就完成了整个步骤,对于输入的字符串,如果能沿着dfa走到接收状态,就说明能够匹配。具体源码看这里 可能有bug,最后的最小化dfa也没有实现,轻喷。

最后推荐几个相关链接

轮子哥的教程 http://www.cppblog.com/vczh/archive/2008/05/22/50763.html

http://www.cnblogs.com/cute/p/4021689.html 这个人写得比较清楚

再推荐一下网易公开课的课程 http://mooc.study.163.com/learn/USTC-1000002001?tid=1000003000#/learn/content

时间: 2024-10-13 07:58:12

简易正则表达式引擎的实现的相关文章

基于ε-NFA的正则表达式引擎

正则表达式几乎每个程序员都会用到,对于这么常见的一个语言,有没有想过怎么去实现一个呢?乍想一下,也许觉得困难,实际上实现一个正则表达式的引擎并没有想像中的复杂,<编译原理>一书中有一章专门讲解了怎么基于状态机来构建基本的正则表达式引擎,讲这个初衷是为词法分析服务,不过书里的东西相对偏理论了些,实现起来还是要费些功夫的,只是它到底指明了一条路,当然,书里只针对基本的语法进行了分析讲解,对于在实际中有些非常有用的很多扩展语法,它就基本没有涉及了,这些扩展的语法中有些是比较好实现的,有些则很难. 基

1000行代码徒手写正则表达式引擎【1】--JAVA中正则表达式的使用

简介: 本文是系列博客的第一篇,主要讲解和分析正则表达式规则以及JAVA中原生正则表达式引擎的使用.在后续的文章中会涉及基于NFA的正则表达式引擎内部的工作原理,并在此基础上用1000行左右的JAVA代码,实现一个支持常用功能的正则表达式引擎.它支持贪婪匹配和懒惰匹配:支持零宽度字符(如"\b", "\B"):支持常用字符集(如"\d", "\s"等):支持自定义字符集("[a-f]","[^b-

Python的regex模块——更强大的正则表达式引擎(未完待写)

Python自带了正则表达式引擎(内置的re模块),不过支持的特性比较单薄,以下这些特性都不支持: 固化分组    Atomic grouping 占有优先量词    Possessive quantifiers 可变长逆序环视    Variable-length lookbehind 递归匹配    Recursive patterns Matthew Barnett为Python写了一个更强大正则表达式引擎——regex模块,可以替代内置的re模块.除了上面说到的几个特性,还有很多新东西.

实现一个 DFA 正则表达式引擎 - 4. DFA 的最小化

(正则引擎已完成,Github) 最小化 DFA 是引擎中另外一个略繁琐的点(第一个是构建语法树). 基本思路是,先对 DFA 进行重命名,然后引入一个拒绝态 0,定义所有状态经过非接受字符转到状态 0,0 接受所有字符转换为自身.也就是说我们先建立一个转换表,然后把第一行填写为: state a b c d e f g h ... 0 0 0 0 0 0 0 0 0 0 再之后,我们讲 DFA 的其余状态从 1 开始重命名,填入状态表.代码实现如下: // rename all states

DEELX 正则表达式引擎(v1.2)

DEELX 正则表达式引擎(v1.2) 简介见文末. 选择使用deelx的理由:全部代码位于一个头文件(.h)中, 比任何引擎都使用简单和方便. 利用分组从字符串当中提取出化学元素英文名.比如 Ag,Au,Fe3O4,Al2O3,K2O,CaO...... 1 #include "deelx.h" 2 #include <string> 3 //该正则表达式识别字符串当中是否有化学元素英文名. 4 //特征:首字母大写,由英文字符和数字组成. 5 //示例:数学变换_Ag_

(2015大作业)茹何优雅的手写正则表达式引擎(regular expression engine

貌似刚开学的时候装了个逼,和老师立了个flag说我要写个正则表达式引擎,然后学期末估计老师早就忘了这茬了,在历时3个月的懒癌发作下,终于在这学期末deadline的时候花了一个下午加晚上在没有网的房间写完了它,于是便有了这篇blog,本来想正儿八紧写篇论文,说不定毕业设计可以直接丢一篇这个走人,但第一觉得一个晚上写好的东西太low了,第二自己实在不适合写那种正经的论文,于是还是写从高中开始的一贯的乱七八糟体好了. 主要写自己写的时候遇到的一些瓶颈,例如茹何储存一个图,茹何遍历一个图,茹何表示一个

实现一个 DFA 正则表达式引擎 - 1. 语法树的构建

语法树的构建这里分为三步: 1. 补全正则表达式的省略部分(主要是省略的 concat 和 or 连接符)并翻译七个集合字 '\w', '\W', '\s', '\S', '\d', '\D' 和 '.': 2. 转换为逆波兰表达式: 3. 转换为语法树: 这里以正则表达式 (a*b|ab*) 为例,逐步解释构建语法树的过程. 1. 补全正则表达式的省略部分 符合我们要求的正则表达式只有三个正交的运算符,或运算,连接运算,重复量词.这里将正则表达式转换为以上三种运算加上两个括号运算符.转换规则比

实现一个 DFA 正则表达式引擎 - 0. 要求

决定把轮子造的飞起,试着用 JAVA 写个正则引擎. 要求: 1.  纯 DFA,无需支持 捕获组 和 断言: 2.  时间复杂度 O(n): 3. 支持 ASCII 字符集,支持基本语法:支持'\w', '\W', '\s', '\S', '\d', '\D' 和 '.'  七个集合及其他单字符转义,支持 ?, *, +, {x}, {x,}, {x,y} 六种量词,支持集合反转,支持括号. 4. 避免使用第三方工具,编译为无依赖库. (正则引擎已完成,Github)

实现一个 DFA 正则表达式引擎 - 3. NFA 的确定化

我们上一节已经将 NFA 构建出来了,我们的 NFAState 对象的结构实际上是这样的: NFAState { private Set<NFAState> directTable; private Map<Character, Set<NFAState>> transitionMap; private int id; } 其中 transitionMap 是该状态接受 Character 后可以转换的状态的映射表. 那么以初始状态 0 为例,我们是不是可以认为状态 0