问题引入

正规式是一种编译原理课程中经常提到的描述文法的语法规则,与正则表达式有相同之处,但并不是同一个概念。有效的正规式的字母表∑ = { a − z , A − Z } 。

所有的正规式中的符号以及对应的规则如下:

‘*’:表示闭包;
‘|’:表示联合(或)——在有的版本中也用’+'来表示;

‘.’:表示连接(与);

NFA(non-deterministic finite automaton with ε-move,ε-NFA)带空移动的非确定的有穷状态自动机

从正规式到NFA的转换是编译原理课程中经典的问题,现在考虑使用C++代码来实现这一过程

解题分析

首先我们考虑如何表示NFA,博主这里采用的方法是将NFA的每一条边都显示并且输出出来,例如:边1 起始节点为:A1 转换过程为:a 结束节点为:A2,并以此方式展示NFA的所有的边。这里就需要构造一个结构体结构体Edge(由两个首尾节点和一个转换过程组成,表示NFA中的一条边)

1、用户输入正规式

2、对正规式进行合法性判断

3、对合法的正规式补全省略的连接符号 ’ . ’

4、将所得的中缀表达式转换为后缀表达式(逆波兰式)

5、“计算”后缀表达式。即将后缀表达式转换为NFA

​ (1)碰到字母表中的字符构造成一个结构体Edge(由两个首尾节点和一个转换过程组成,表示NFA中的一条边),进行压栈

​ (2)碰到运算符,从栈顶弹出一个(单目运算符)或两个字母(双目运算符),并进行对应的操作

函数Unite(Edge Left, Edge Right),处理 a|b的情况,并返回一个Edge类型

处理方法:新增两个空的节点,四条边**(红色所示)**,并将原有的a、b连起来,如图所示,返回的Edge的变量起始节点为新生成的A5,结束节点为新生成的A6,转换过程Thro置空 ‘ ’

函数Join(Edge Left, Edge Right),处理 a.b的情况,并返回一个Edge类型

处理方法:把右边Right的起始节点Begin 改成Left的结束节点End,并返回一个新的Edge的对象,起始节点为Left的开始节点,结束节点为Right的结束节点,转换过程Thro置空 ‘ ’

函数Self(Edge edge) ,处理 a*的情况,并返回一个Edge类型

处理方法:新增两条边**(红色所示)**构成闭环,返回传过来的参数edge,但需要把edge的转换过程变量Thro置空 ‘ ’

代码实现

RexpToNFA.h

#include <iostream>
#include <cmath>
#include <string>
#include <stack>
#include <queue>
using namespace std;int Aflag = 0;//a用于标记NFA中的状态,例如:A0、A1class Edge {
public:Edge() {this->begin = "";this->thro = ' ';this->end = "";};Edge(string begin, char thro, string end) {this->begin = begin;this->thro = thro;this->end = end;}void setThro(char ch) {this->thro = ch;}char getThro() {return this->thro;}void setBegin(string ch) {this->begin = ch;}string getBegin() {return this->begin;}void setEnd(string ch) {this->end = ch;}string getEnd() {return this->end;}private:char thro;string begin;string end;
};class RTN {
public:RTN() {};bool isletter(char ch) {if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {return true;}else {return false;}}bool islegal(string s) {int Lbracket = 0;int Rbracket = 0;RTN r;for (int i = 0; i < s.length(); i++) {char ch = s[i];if (ch == '*' || ch == '|' || ch == '.') {continue;}//数字不考虑判断else if (ch >= 0 && ch <= 9) {continue;}else if (r.isletter(ch)) {continue;}else if (ch == '(') {Lbracket++;}else if (ch == ')') {Rbracket++;}else {return false;}}if (Lbracket != Rbracket) {return false;}else {return true;}}//将输入的表达式中的连接符号用'.'补全string turnToConnect(string s) {string ns = s.substr(0, 1);for (int i = 1; i < s.length(); i++) {char prech = s[i - 1];char ch = s[i];//连续的字母之间需要添加上连接符号'.'if (isletter(prech) && isletter(ch)) {ns = ns + '.' + ch;//cout << endl << ns;continue;}//判断当前是字母,并且前面是右括号')'的情况else if (isletter(ch) && prech == ')') {ns = ns + '.' + ch;continue;}//判断当前是左括号'(',并且前面是右括号')'或者前面是字母的情况else if (ch == '(' && prech == ')' || ch == '(' && isletter(prech)) {ns = ns + '.' + ch;continue;}else {ns += ch;//cout << endl << ns;continue;}}return ns;}string ReversePolishType(string s) {stack<char> st;string ns = "";for (int i = 0; i < s.length(); i++) {char ch = s[i];if (isletter(ch)) {ns = ns + ch;}else if (ch == '(') {st.push(ch);}else if (ch == ')') {while (st.top() != '(') {ns = ns + st.top();st.pop();}st.pop();//将栈顶剩余的左括号弹出}else if (ch == '*') {ns = ns + ch;//'*'是单目运算符,碰到的时候直接弹出/*if (st.empty() || st.top() == '.' || st.top() == '|' || st.top() == '(') {st.push(ch);}else {while (st.top() == '*') {ns = ns + st.top();st.pop();if (st.empty()) break;}if (st.empty() || st.top() == '.' || st.top() == '|' || st.top() == '(') {st.push(ch);}}*/}else if (ch == '.') {if (st.empty() || st.top() == '|' || st.top() == '(') {st.push(ch);}else {while (st.top() == '*' || st.top() == '.') {ns = ns + st.top();st.pop();if (st.empty()) break;}if (st.empty() || st.top() == '|' || st.top() == '(') {st.push(ch);}}}else if (ch == '|') {if (st.empty() || st.top() == '(') {st.push(ch);}else {while (st.top() == '*' || st.top() == '.' || st.top() == '|') {ns = ns + st.top();st.pop();if (st.empty()) break;}if (st.empty() || st.top() == '(') {st.push(ch);}}}}while (!st.empty()) {ns = ns + st.top();st.pop();}return ns;}//处理 a|bEdge Unite(Edge Left, Edge Right) {Edge ed1(to_string(Aflag), '#', Left.getBegin());Edge ed2(to_string(Aflag), '#', Right.getBegin());Edge ed3(Left.getEnd(), '#', to_string(Aflag + 1));Edge ed4(Right.getEnd(), '#', to_string(Aflag + 1));q.push(ed1);q.push(ed2);q.push(ed3);q.push(ed4);if (Left.getThro() != ' ') {this->q.push(Left);}if (Right.getThro() != ' ') {this->q.push(Right);}Edge ed(to_string(Aflag), ' ', to_string(Aflag + 1));Aflag = Aflag + 2;return ed;}//处理 a.bEdge Join(Edge Left, Edge Right) {Right.setBegin(Left.getEnd());if (Left.getThro() != ' ') {q.push(Left);}if (Right.getThro() != ' ') {q.push(Right);}Edge ed(Left.getBegin(), ' ', Right.getEnd());//Edge ed(Left.getBegin(), Left.getThro() + '.' + Right.getThro(), Right.getEnd());return ed;}//处理 a*Edge Self(Edge edge) {Edge ed1(edge.getBegin(), '#', edge.getEnd());Edge ed2(edge.getEnd(), '#', edge.getBegin());this->q.push(ed1);this->q.push(ed2);/*Edge ed1(to_string(Aflag), '#', edge.getBegin());Edge ed2(edge.getEnd(), '#', to_string(Aflag + 1));Edge ed3(edge.getEnd(), '#', edge.getBegin());Edge ed4(to_string(Aflag), '#', to_string(Aflag+1));this->q.push(ed1);this->q.push(ed2);this->q.push(ed3);this->q.push(ed4);*/if (edge.getThro()!=' ') {this->q.push(edge);}Aflag = Aflag + 2;Edge ed(to_string(Aflag), ' ', to_string(Aflag + 1));return ed;}void PolishTypeToNFA(string s) {stack<Edge> st;Aflag = 0;//a用于标记NFA中的状态,例如:A0、A1for (int i = 0; i < s.length(); i++) {char ch = s[i];if (this->isletter(ch)) {Edge ed(to_string(Aflag), ch, to_string(Aflag + 1));Aflag = Aflag + 2;st.push(ed);}else if (ch == '*') {Edge ed = st.top();st.pop();st.push(this->Self(ed));}else if (ch == '.') {Edge ed1 = st.top();st.pop();Edge ed2 = st.top();st.pop();Edge ed = Join(ed2, ed1);st.push(ed);}else if (ch == '|') {Edge ed1 = st.top();st.pop();Edge ed2 = st.top();st.pop();st.push(this->Unite(ed1, ed2));}}while (!this->q.empty()){Edge e = q.front();cout << endl << "开始结点为:" << e.getBegin() << "     转换过程为:" << e.getThro() << "     结束结点为:" << e.getEnd() ;q.pop();}}private:queue<Edge> q;
};

RexpToNFA.cpp

#include <iostream>
#include <cmath>
#include <string>
#include <stack>
#include <queue>
#include "RexpToNFA.h"
using namespace std;
int main() {RTN rt;cout << "有效的正规式的字母表∑={a-z,A-Z},辅助字母表∑={'*','|','(',')','.'}" << endl;cout << "'*'   表示闭包"<< endl <<"'|'   表示联合" << endl << "'.'   表示连接" << endl;while (1) {cout << "\n请输入一个正规式:";string s = "";cin >> s;if (rt.islegal(s)) {cout << "输入的正规式合法!"<<endl;string ss = rt.turnToConnect(s);cout << "补缺省略的连接符号'.'之后为:" <<ss << endl;cout << "对应的后缀表达式(逆波兰式)为:" <<rt.ReversePolishType(ss);string rs = rt.ReversePolishType(ss);rt.PolishTypeToNFA(rs);}else {cout << "输入的正规式不合法!"<<endl<<"请重新输入:";}}}

正规式转换为NFA代码实现相关推荐

  1. 编译原理——正规式、NFA转换构造DFA、DFA的化简

    一.DFA和NFA的区别 NFA:非确定有限自动机 DFA:确定有限自动机 NFA在同一状态,可以有多条出边,DFA在同一状态,只能有一条出边: NFA的初态可以具有多个,DFA的初态是唯一的: 比如 ...

  2. 编译原理词法分析(正规式转NFA)

    文章目录 [问题描述] [基本要求] [测试用例] [解决步骤] 正规式转NFA方法步骤: 图的构建过程: 完整代码: [问题描述] 正规表达式→NFA问题的一种描述是: 编写一个程序,输入一个正规表 ...

  3. 编译技术:正规式、NFA、DFA、最简DFA的转换

    正规式.NFA.DFA.最简DFA的转换 在编译原理中,正规式.NFA(非确定有穷自动机).DFA.最简DFA的转换在词法分析中是十分重要的一个环节. 一般来说:我们经常碰到的问题类型都是如下类型的: ...

  4. 正规式生成NFA和DFA

    问题描述 给定正规式,自动生成NFA和DFA,并以表得形式输出NFA和DFA. 算法设计 整行读入正规式后,逐字遍历,生成item数组,item数组第一个元素是空转移,用e表示.然后重新遍历正规式,每 ...

  5. 1203正规式转换为有穷自动机

    1 #include<stdio.h>2 #include <ctype.h>3 #define ok 14 #define error 05 #define MAXREGLU ...

  6. 正规式到nfa dfa java_正规式与正规集,DFA与NFA

    词法分析器的设计 词法分析器的功能:输入源程序.输出单词符号 词法分析器的设计:给出程序设计语言的单词规范--单词表, 对照单词表设计识别该语言所有单词的状态转换图, 根据状态转换图编写词法分析程序 ...

  7. (编译原理)正规文法转正规式(原代码)

    (编译原理)正规文法转正规式 一.实验要求 输入:正规文法 输出:正规式 例: 输入:S->aB B->b 输出:ab 输入:S->aS S->b 输出:a*b 输入:S-&g ...

  8. 正规式到最小化DFA

    整体的步骤是三步: 一.先把正规式转换为NFA(非确定有穷自动机) 二.在把NFA通过"子集构造法"转化为DFA 三.在把DFA通过"分割法"进行最小化 一.正 ...

  9. 词法分析程序之正规式转换成NFA

    文章目录 前言 正规式变成NFA 前备知识 中缀表达式和后缀表达式(细节处理) 输入正规式和转换NFA 测试结果 完整代码 前言 编译原理课里面书本有一个作业--使用C++实现: 将正规式变成NFA ...

最新文章

  1. 关于iOS知识的提升
  2. 谈谈技术原则,技术学习方法,代码阅读及其它
  3. [绝对原创]一些你们想不到的简单方法,就可以让你手机飞快起来!!!
  4. Codeforces Round #250 (Div. 1) D. The Child and Sequence 线段树 区间取摸
  5. Jenkins+Gitlab+ansible-playbook上线流程
  6. Norton Ghost V12
  7. 软件测试预演环境,什么是软件测试用例预演 有何优点?[3]
  8. win10黑科技,新建桌面非常好用
  9. win10系统安装教程(U盘PE+UEFI安装)
  10. 企业微信开发之判断当前入口是PC端企业微信还是PC端浏览器。或者是APP端企业微信
  11. Windows下模拟弱网(web、app)均可使用
  12. 位运算:【leedcode:只出现一次的数字】
  13. substrate介绍
  14. 一个资深程序员看12306 (三)
  15. 高级软件工程第九次作业:东理三剑客团队作业-随笔5
  16. 1.NR中PointA、Offsettocarrier、RIV等计算
  17. 用正则表达式来判断手机号、地址、身份证号、邮箱等格式是否正确
  18. CentOS7 安装 RabbitMQ 3.6(方法适用于安装任意版本 RabbitMQ)
  19. 【科研思考】如何做学术研究?—— 我的方法论
  20. 仙居机器人_【101梦想秀】不得了!他是仙居机器人领域的领跑者!

热门文章

  1. 经典算法(5)杨辉三角
  2. python清屏失败_python idle 清屏问题的解决
  3. 图片服务器是用什么协议,CCO协议是什么意思?CCO协议的图片能商用吗?
  4. Redis cache-aside模型-分布式锁等问题研究
  5. 德国大众跃居世界第一大汽车生产商
  6. pcm 降采样_深度卷积神经网络中的降采样
  7. nsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is st
  8. 代码随想录算法训练营第一天 | 704.二分查找、27.移除元素题目
  9. 理解SSL/TLS系列 (五)握手协议
  10. mysql关联查询操作表最新数据