C++实现正则表达式转最小化DFA过程详解

目前学校正在开编译原理这门课。为了加深对正则表达式转最小化DFA的理解,利用c++编写了这个程序。现在把我的程序及实现的方法分享出来,希望能对大家的学习有所帮助。

完整代码:https://github.com/GgBondXiang/RexToMinDFA

正则表达式

操作数

本程序的支持的操作数为小写字母‘a’~‘z’

运算符

正则表达式包含三个运算符

1)或运算符“|”

2)连接运算符“.”,一般省略不写,本程序中用“&”代替

3)闭包运算符“*”,即任意有限次的自重复连接

规定算符的优先顺序为先“*”,再“.”,最后“|”。

算法流程

1.正则表达式的预处理

预处理是把表达式中省略的连接运算符加上,方便计算机运算

需要添加“&”的有六种情况,分别为“a a” 、“a (” 、“* a” 、“* (” 、“) a” 、 “) (”
总结起来为当第一位是操作数、“*”、“)”且第二位为操作数或“(”

以表达式"(a|b)* abb"为例,预处理后的表达式为:“(a|b)* &a&b&b”

2.中缀表达式转后缀表达式

运算符的优先级为 闭包‘*’ > 连接‘&’ > 或‘|’

转换过程中需要用到一个运算符栈,具体过程如下:

  • 如果遇到操作数,直接将其输出。
  • 如果遇到运算符:
    • 遇到‘(’直接压入栈中;
    • 遇到‘)’将运算符出栈并输出,直到遇到‘(’,将‘(’出栈但不输出;
    • 遇到‘*’、‘&’、‘|’运算符:
      • 如果栈为空,直接将运算符压入栈中;
      • 如果栈不为空,弹出栈中优先级大于等于当前运算符的运算符并输出,再将当前运算符入栈。
  • 当输入串读取完之后,如果栈不为空,则将栈中元素依次出栈并输出。
/*
*本程序中比较优先级的方法是为每一个运算符返回一个权值,通过权值的大小来比较优先级。
*但是实际操作中会遇到bug,这是因为程序无法返回左括号的优先级。
*为了保证弹出优先级大于等于当前运算符的运算符时,左括号不会出栈,将左括号的权值设为0
*/
int priority(char ch)
{
   

	if(ch == '*')
	{
   
		return 3;
	}
		
	if(ch == '&')
	{
   
		return 2;
	}
		
	if(ch == '|')
	{
   
		return 1;
	}
	
	if(ch == '(')
	{
   
		return 0;
	}
}

下面看这个例子“(a|b)* &a&b&b”

1.遇到“(”,直接入栈
栈:(
输出区:
2.遇到“a”,直接输出
栈:(
输出区:a
3.遇到“|”,栈中没有优先级大于等于它的运算符,直接压入栈中
栈:( |
输出区:a
4.遇到“b”,直接输出
栈:( |
输出区:a b
5.遇到“)”,“|”出栈并输出,左括号出栈
栈:
输出区:a b |
6.遇到“*”,栈为空,直接入栈
栈:*
输出区:a b |
7.遇到“&”,“*”出栈并输出,再将“&”入栈
栈:&
输出区:a b | *
8.遇到“a”,直接输出
栈:&
输出区:a b | * a
9.遇到“&”,“&”出栈并输出,再将“&”入栈
栈:&
输出区:a b | * a &
10.遇到“b”,直接输出
栈:&
输出区:a b | * a & b
11.遇到“&”,“&”出栈并输出,再将“&”入栈
栈:&
输出区:a b | * a & b &
12.遇到“b”,直接输出
栈:&
输出区:a b | * a & b & b
13.字符串读完,栈不为空,将栈中元素出栈并输出
栈:
输出区:a b | * a & b & b &

所以转换完的后缀表达式为“ab|* a&b&b&”

3.后缀表达式创建NFA

首先介绍一下NFA的存储结构,下图为一个NfaState
NfaState

struct NfaState				/*定义NFA状态*/
{
   
	
	int index;				/*NFA状态的状态号*/ 
	
	char input;				/*NFA状态弧上的值,默认为“#”*/
	int chTrans;			/*NFA状态弧转移到的状态号,默认为-1*/ 
	
	IntSet epTrans;			/*当前状态通过ε转移到的状态号集合*/ 
编译原理是学习计算机科学的一门基础课程,主要涉及语言的识别和化,而正则表达式则是其中一个非常重要的工具。在编译原理中,正则表达式通常用于描述一些模式,比如关键字、标识符等。因此,掌握正则表达式过程对于理解编译原理课程非常重要。 正则表达式过程主要包括以下几个部分:正则表达式NFA、NFADFADFA最小化。其中,NFA(非确定有限状态自动机)和DFA(确定有限状态自动机)都是描述正则表达式的模型。 正则表达式NFA: 首先,正则表达式中的基本元素是字符、括号和运算符。在换为NFA的过程中,需要设计出一些状态来描述不同的字符和运算符。 对于字符来说,我们可以为它们设计出一个状态,状态的入口边是字符,出口边为空。 对于括号和运算符来说,可以为它们设计出一些连接状态。例如在括号中的字符可以通过连接状态直接连接到后面的状态,或者通过其他运算符先连接到其他的状态再连接到后面的状态。 最后,需要定义一个起始状态和一个终止状态,起始状态与第一个字符状态相连,最后一个字符状态与终止状态相连。这样,我们就得到了一张NFA图。 NFADFA: 将一个NFA图换成DFA图的主要目的是为了简化图结构,以便后续对文本进行识别。 首先,需要定义DFA的状态集合,每个集合都对应一个状态。因为DFA是完全确定的有限状态自动机,所以在DFA中只能有一个状态。 然后,需要将NFA图中的每个状态都映射为DFA图中的一个状态,以便对文本进行识别。当NFA图中有多个状态对应于DFA图中的同一状态时,需要将它们合并,并将它们的出口边合并成一个出口边。 DFA最小化: 最后,对DFA进行最小化处理,以便减少状态数,提高运行效率。在最小化处理时需要考虑不同状态之间的等价关系。 可以采用遍历算法,将DFA中的状态按照等价关系划分为若干个等价类,然后构造一个等价类访问表,每个表项对应一个状态集。 最小化后的DFA图是可以识别文本的,可以用于在编译器中进行文本匹配和词法分析等操作。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值