- 1. DFA状态转换图
- 2. 问题描述
- **输入格式**:
- **输出格式**:
- **输入样例1:**
- **输出样例1:**
- **输入样例2:**
- **输出样例2:**
- **输入样例3:**
- **输出样例3:**
- **输入样例4:**
- **输出样例4:**
- 3.问题分析
- 4. 流程图
- 5.代码
- Mot de l’auteur(作者的话):
- Au revoir à tous!
该C++程序是基于以下特定DFA编写的简单词法分析器,如需更改其他DFA,下文讲解释需要改动的地方。
输入一个字符串。
输出格式:若字符串正确,输出单词串。如果有错误,输出错误的类型。
输入样例1:bacbacdabbaccb输出样例1:
bacb acdab baccb 字符串共被划分为3个词输入样例2:
abaxab输出样例2:
ab a 字符串中第4个字符x是一个非法字符输入样例3:
abaaba输出样例3:
ab a 字符串中第4个字符a发生了错误输入样例4:
aba输出样例4:
ab a 最后一个词不完整3.问题分析
我们都知道“程序” = “数据结构”+“算法”,所以第一个要解决的问题便是DFA和字符串的存储问题。其中最值得思考的是DFA的存储问题。
字符串看似好处理,但是也隐藏很大的问题。比如,应该用c++的string数据类型,还是应该使用char数组来存储呢?在字符串输入的时候,如何防止空格和回车进入缓冲区呢?
鉴于我们接下来要对每个字符进行分析,所以我采用char数组的方式存储字符串。
而DFA我是用状态转移矩阵来存储,只需要一个二维数组便可以解决。
下图是草稿思路过程(草稿嘛,乱一点很正常)和流程图。
#includeMot de l’auteur(作者的话):using namespace std; //构造的DFA const int DFA[4][4] = { {1, 0, -1, -1}, {-1, 3, 1, 2}, {1, -1, -1, -1}, {-1, -1, -1, -1}, }; //开始状态 const int START_STATE = 0; //结束状态 const int END_STATE = 3; //合法字符集 默认字母与下表之间一一对应 a-0 b-1 c-2 d-3 const char ALPHABET[4] = {'a', 'b', 'c', 'd'}; //错误类型集 const char ERROR_TYPE[3][100] = {"n字符串中第%d个字符%c是一个非法字符", "n字符串中第%d个字符%c发生了错误", "n最后一个词不完整"}; int main() { //输入的字符串 char str[101]; cin >> str; //DFA行号 int lineNum = 0; //统计划分成几个单词 int wordCnt = 0; //目前在执行字符串中的哪个字符 // int charCnt = 0; //字符串长度 int strCnt = strlen(str); //字符下标 int index = 0; //错误类型 -1代表没有出现错误 int errorType = -1; //当前状态 int currentState = 0; //开始词法分析 while (strCnt--) {//从前往后扫描所有字符 //状态转移 if (str[index] == 'a') { int state = DFA[lineNum][0]; if (state != -1) { //状态转移路径存在 //进行状态转移 lineNum = state; //更新当前状态 currentState = lineNum; //输出当前字符 cout << str[index]; } else { //状态转移路径不存在 errorType = 1; break; } }else if(str[index] == 'b'){ int state = DFA[lineNum][1]; if (state != -1) { //状态转移路径存在 //进行状态转移 lineNum = state; //更新当前状态 currentState = lineNum; //输出当前字符 cout << str[index]; } else { //状态转移路径不存在 errorType = 1; break; } }else if(str[index] == 'c'){ int state = DFA[lineNum][2]; if (state != -1) { //状态转移路径存在 //进行状态转移 lineNum = state; //更新当前状态 currentState = lineNum; //输出当前字符 cout << str[index]; } else { //状态转移路径不存在 errorType = 1; break; } }else if(str[index] == 'd'){ int state = DFA[lineNum][3]; if (state != -1) { //状态转移路径存在 //进行状态转移 lineNum = state; //更新当前状态 currentState = lineNum; //输出当前字符 cout << str[index]; } else { //状态转移路径不存在 errorType = 1; break; } }else{ //出现非法字符 errorType = 0; break; } //到达终止状态 if(currentState == 3){ lineNum = 0; wordCnt++; putchar(10); } index++; }//end while //因为while的特性,多减了一个,加回来 strCnt++; //第三种出错处理 if(strCnt == 0 && currentState != 3){ //最后一个词不完整 errorType = 2; } //输出错误 switch (errorType) { case 0: printf(ERROR_TYPE[errorType],(index+1),str[index]); break; case 1: printf(ERROR_TYPE[errorType],(index+1),str[index]); break; case 2: printf(ERROR_TYPE[errorType]); break; default: printf("字符串共被划分为%d个词",wordCnt); break; } return 0; }
因为一些历史遗留问题(就是懒),前期分析问题的时候预先设定的一些变量,没有使用到,后面写完也懒得删了哈哈哈~
关于更换DFA:你只需要修改状态转移函数(const int DFA[4][4])便可以,我已经提升作用域到全局变量了,在程序的开头就能看见。
Au revoir à tous!


