有限状态自动机java实现_用java开发编译器之:Thompson构造,将正则表达式转换为有限状态自动机...

阅读博客的朋友可以到我的网易云课堂中,通过视频的方式查看代码的调试和执行过程:

上一节,我们通过代码,实现了一个有限状态自动机,并将其应用于对整形和浮点数的识别。构造有限状态自动机,并驱动它,从而实现对输入字符串的识别,整个过程就是词法分析的本质。

上一节所开发的状态机,基于以下模型:

e268e622f14eaaa7bd986f0c938ab8c4.png

这个模型,是我们在代码中,手动写入程序的。实则上,它对应着一组正则表达式:

D       [0-9]  表示0-9的字符类

{D}+    表示由 0-9 构成的整形数值

({D}+ | {D}*\. {D}+ | {D}+ \. {D}*)(e{D}+)?表示浮点数或科学计数法

其中{D}+ 对应着状态机中,由0到1,然后在1中自转这一流程。最后一个正则表达式,对应图中由状态0到状态2,或4的流程。

那么,问题来了,给定一个正则表达式,可否直接生成一个有限状态自动机呢?答案是肯定的,大多数正则表达式识别程序,基本上都是先将其转换为自动机,然后通过驱动自动机来识别输入的,将正则表达式转换为有限状态自动机将是我们这几节的重点。

有限状态自动机的分类

有限状态自动机,其实可以分成两类。第一类是我们上面给出的,叫做确定性有限状态自动机: Deterministic finite automaton 简称DFA. 确定性的状态机有一个特点,就是给定当前状态和输入字符,那么下一个状态就能被唯一确定。例如基于上图,在状态1时,接收到字符0-9,那下一个状态一定只能是1,如果接收到字符 . ,那下一个状态,就一定只能是2. 更严谨的说, DFA 是这样一种自动机,从给定状态出去的边都对应着一个确定的字符,同时,从一个状态出去的两条边,他们对应的字符必定是不同的。

对应于DFA, 另一种状态机叫非确定性有限状态机: Nondeterministic finite automaton, 即NFA. 在实践中,要想顺利的将正则表达式转换为自动机,需要NFA的帮助。NFA 的特点是,从一个状态出去的两条边,可以有相同的对应字符。或者它的边可以对应一种特殊的字符叫”空”字符,该字符对应的符号是: ?.这种边表示,不需要任何输入,就可以从当前状态进入下一个状态。

举个例子,表达式(and | any) , 它对应的DFA如下:

adef2f164ffbf6826403093fcd120e5c.png

它对应的NFA 如下:

从初始状态开始,分化出两条边,两条边对应的字符是一样的

b5f75fbecba9cf0e1d4314602596ff49.png

或者:

从初始状态分化两条对应字符为空字符的边,然后分别进入两个对应的状态机

11808ecdd0df321dd789c7c91204451c.png

第二种NFA在程序设计中容易实现,因此,在下一节的代码中,我们将采用第二种NFA的实现模式。

NFA有一个明显的弱点就是,在代码设计中,很难用数据结构来对它进行表示。特别是,当对应于一个输入字符,NFA可以跳转到多个状态,那么,要想利用NFA去识别输入字符串就比较困难。一般而言,使用NFA的程序都需要经过下两个步骤:将正则表达式转换为NFA, 将NFA转换为DFA. 在后面的讨论中,我们将通过代码来展示这两种转换.

Thompson 构造法

将正则表达式转换为NFA的算法是由贝尔实验室的Ken Thompson 给出的,这哥们跟丹尼斯.里奇共同开发了Unix, 而他开发了C语言的前身 B 语言。

他的算法如下:

最简单的正则表达式是单字符匹配,例如a 匹配输入字符”a”, 那么该表达式的NFA 构造如下:

682604f043c44ced4e9e43d4cfe83639.png

那么,两个这样的正则表达式合成的连接表达式ab 可以表示如下:

b954bdf2e9d3e816f1947e79f2b227a4.png

实际上,它是先分别构造出两个表达式的NFA, 然后通过一条?边,将两个NFA首尾连接起来。

下面我们看看,两个表达式进行 OR 操作的时候 | ,NFA怎么构造,构造图如下:

e27d4f4569da71b02cd567cc4563c83a.png

要构造两个表达式的或操作: exp1 | exp2, 根据图示,首先分别构造两个表达式exp1 , exp2 各自的NFA: NFA1(上头虚线框), NFA2(下头虚线框), 然后再构造两个状态,初始状态(开头圆圈节点),和结束状态(末尾圆圈节点),初始状态延生处两条 ? 边,分别指向NFA1 和 NFA2 的开头,然后NFA1 和 NFA2的结尾各自延生出一条?边,分别共同指向结束状态。

我们再看看 a | b 的NFA图:

0db65b010d04dea657f76539cccc1b0d.png

其原理跟前面所描述的是一样的。上头虚线框是表达式 a 的NFA, 下头虚线框是表达式 b 的NFA. 两个NFA的连接跟前面描述的一模一样

如果表达式是( (a|b) | cd) 呢,算法也同理,先构造 a | b 的NFA图,然后构造cd的NFA图。最后根据前面所说的办法,再将两个NFA连接起来:

上头大虚线框是 (a|d) 的NFA, 下头长匾虚线框是 cd的NFA. 然后首尾通过两个状态节点和ε边连接起来。

0db65b010d04dea657f76539cccc1b0d.png

大家可以看到, Thompson构造算法其实是一个自我递归的过程

我们再看看相应的闭包操作的构造过程:

exp*的NFA:

如果是自我从复0次,那直接从下面的边走到末尾节点。

c15ca71acb0e041bf353bbe8897c6eb3.png

exp+(至少重复一次) 的NFA:

5fa9b6bbc9b9d76de24e52f6891a632b.png

exp?(重复0或1次)的NFA:

bf660a30b813ae56eac82a7a7b3be1ba.png

任何复杂的正则表达式它的NFA的构造都是上面几种构造的组合, 例如表达式

(D*\.D| D\.D*)

构造算法如下:

1.  构造 D 的NFA:

36599b323c57639ed1197cf140a20cd7.png

2.  构造 D*:

fa33c95984e6774c0a2ebf73799ea077.png

3.  构造 D*\.D (由于.在正则表达式中是特殊字符,如果要仅仅想要表达它的符号内容,要在前面加上反斜杠做转义):

. 号的前部分是D*, 后部分是 D 的NFA.

8f2e77b9dcf60d79671e29b4c5fbc16a.png

4.  构造 D\.D*, 该表达式的NFA其实就是将上图 . 后面的部分挪到开头。

5.  根据OR 的构造法, 构造整个表达式 (D*\.D | D\.D*)的NFA:

上头是 D*\.D 的NFA, 下头是 D\.D*的NFA

8fd16f7a20893c9b6ab7b83b0aacb328.png

再复杂的表达式的NFA的构造,都是几种基础构造的重复组合运用。

我们这一节对概念和算法的介绍就到这里,根据我的习性,下一节肯定就是上代码了。

原文:http://blog.csdn.net/tyler_download/article/details/51072362

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409590.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zend Server搭建网站备注

1、Zend Server的使用 Zend Server自带php和apache,所以装zendserver的话不需要再安装其他任何东西。(mysql默认没有安装,可以自行设置) 2、工作路径配置: 在Zend\Apache2\conf\httpd.conf中添加 Alias/test "F:/workbench/t…

python的符号函数得到的数字类型_Python笔记——数字类型的几个函数

标准类型内建函数&#xff1a;cmp(obj1, obj2) 比较obj1和obj2&#xff0c;根据比较结果返回整型i:i < 0 if obj1 < obj2i > 0 if obj1 > obj2i 0 if obj1 obj2repr(obj)或obj 返回一个对象的字符串表示str(obj) 返回对象适合可读性好的字符串表示type(obj) 得到…

将SqlServer的数据导出到Excel/csv中的各种方法 .

以下都只是介绍操作的原理&#xff0c;具体要求要在应用中具体分析改变。 如果大家有其他好的方法&#xff0c;请相互告知&#xff0c;共同学习。 1. 此方法常用在form或者Console Application中&#xff0c;使用时须用要添加Reference&#xff0c;具体做法&#xff1a; …

java单例模式的实现方法_JAVA单例模式的几种实现方法

1 饿汉式单例类.在类初始化时&#xff0c;已经自行实例化class EagerSingleton {private static final EagerSingleton m_instance new EagerSingleton();/** * 私有的默认构造子 */private EagerSingleton() {}/*** * 静态工厂方法*/public static EagerSingleton getInstanc…

python链表排序_链表排序+末尾各种排序

#工具人排序def nums_sort(data):if not data:return []min_data min(data)max_data max(data)nums [0]*(max_data-min_data1)for value in data:nums[value-min_data]1cur min_datafor index in range (len(data)):while cur< max_data and nums[cur-min_data]<0:cu…

sl animation sample

http://samples.msdn.microsoft.com/Silverlight/SampleBrowser/index.htm#/?srefdoubleanimation 转载于:https://www.cnblogs.com/songtzu/archive/2012/09/05/2672445.html

python中cumsum_在python里“np.cumsum”这个命令是干什么的?怎么使用?

展开全部累计2113求和的命令。5261具体例子如下所示4102&#xff1a;>>> a np.array([[1,2,3], [4,5,6]])>>> aarray([[1, 2, 3],[4, 5, 6]])>>> np.cumsum(a)array([ 1, 3, 6, 10, 15, 21])>>> np.cumsum(a, dtypefloat) # specif…

java rgb转yuv_【转】总结各种RGB转YUV的转换公式

最近在学习视频的颜色空间转换&#xff0c;由于摄像机拍出来的视频很多都是用YUV格式保存的&#xff0c;而颜色空间的转换必须在RGB颜色模型上才能完成&#xff0c;所以第一步自然就是将YUV颜色模型转成RGB颜色模型。在网上查到了许多的YUV与RGB互转的公式&#xff0c;但是总觉…

EDM数据库营销是什么?-EDM数据库营销的概念

可能有些朋友对EDM数据库营销的概念不是很了解。本文就为大家详细讲解一下EDM数据库营销是什么。 EDM数据库营销依然是大部分营销公司的主打产品。但是伴随着B2C的EDM数据库营销的市场不断扩大&#xff0c;一些EDM数据库营销公司已经感觉到其中的商机&#xff0c;纷纷建立自己的…

python两列数据生成邻接矩阵_用python实现邻接矩阵转换为邻接表,python语言实现...

graph {A: [B, C],B: [C, D],C: [D],D: [C,G,H],E: [F],F: [C]}#从图中找出任意一条从起始顶点到终止顶点的路径def find_path(graph, start, end, path[]):if start end:print "path", pathreturn Trueif not graph.get(start):path.pop()return Falsefor v in gr…

驱动程序开发的模具

自从有了操作系统后&#xff0c;就诞生了一种职位叫&#xff1a;驱动程序开发。本文描叙驱动开发所需要的学习方式。 开发驱动程序和编写应用程序之间的区别从招聘岗位可以看出&#xff1a;程序设计师和驱动开发师&#xff0c;它们是设计和开发的区别。设计重点在需求阶段&…

java圆形进度条_可拖拽圆形进度条组件(支持移动端)

好久之前写过一个可拖拽圆形进度条的dome&#xff0c;中间有网友反馈过一些问题&#xff0c;最近比较闲有时间修改了一些问题也做了一些优化&#xff0c;并封装成组件&#xff0c;基于canvas实现&#xff0c;只需传入放置组件dom容器&#xff0c;任何框架均可直接使用&#xff…

java注解 interface_java @FunctionalInterface注解详解

下面要给大家介绍的就javaFunctionalInterface注解&#xff0c;对于FunctionalInterface注解不大了解的人&#xff0c;可以通过下面的文章来了解一下哦。在学习Lambda表达式的时候&#xff0c;假如&#xff0c;接口中只有一个抽象方法(可以包含多个默认方法或多个 static方法)&…

如何用python新建文件夹_用Python编写一个每天都在系统下新建一个文件夹的脚本...

这个程序的功能非常的简单&#xff0c;就是每天在系统中新建一个文件夹。文件夹即当前的时间。此代码是在同事那边看到的&#xff0c;为了锻炼下自己薄弱的Python能力&#xff0c;所以花时间重新写了一个。具体代码如下&#xff1a;import time,osbasePath F:\\work\\thisYear…