主要思路:从一个文件中,得到一个个的字符,然后对字符进行判断处理,在判断的时候会用到DFA(有限自动机)进行状态之间的转换。
得到的单词主要分为几种类型:标识符、保留字、数字、标点符号、将他们分别进行处理,整理后放在一个token序列中。
token主要由两个部分组成:1)类型 2)内容
程序用到的知识点:
1)C语言文件的读写
举例:
if ((fw = fopen("D:\\jg.txt", "w")) == NULL)
{
printf("文件打开失败\n");
exit(1);
}
else {....}
“w”参数部分是指文件的打开方式为只写
“r”只读
“w+”以读写打开方式打开文件,若无此文件,创建文件
“r+”以读写的方式打开文件,若无此文件,出错
*************************************************************************************************************************************************本代码中使用的文件处理函数:
fgetc(f); //其中f为文件类型指针
ungetc(ch,f);//此函数的作用是将文件类型的指针返回一个位置,char型变量ch,写入这个位置
fprintf(".....",.......); //将内容写入文件中
2)有限自动机的实现
主要是利用 if .....else.....语句进行状态之间的转换。
3)全局变量和容器的使用
容器的库include
申请一个库:vector<类型>名称
实验环境:VS2017
#include
#include
#include
#include
#include
using namespace std;
//定义两个表,分别为标识符表和常量表,为全局
vectorbta;
vectorcta;
int error = 0;//定义一个全局变量用来保存是否这个文件存在词法问题
//定义TOKEN结构
class Token
{
public:
Token(string str, int i) //构造函数
{
type = str;
id = i;
};
~Token() {};
string type;
int id;
};
class Scan
{
public:
bool reservedLookup(string str);//判断是是否为保留字
Token* scanner(FILE *f);
int addbta(string str);//查表,若表中含有该标识符返回标识符的id,
//否则将该标识符加入表中
int addcta(string str);
bool Isother(char a);//判断是否为字母和数字以外其他的
bool Isletter(char a);
bool Isnumber(char a);
};
bool Scan::reservedLookup(string str)
{
printf("%s\n", str.c_str());
string ta[21] =
{ "begin","integer","char","program","array","of","record","end","var","proceduce"
,"if","then","else","fi","while","do","endwh","read","write","return","type" };
for (int i = 0; i < 21; i++)
{
if (str == ta[i])
{
cout << "为保留字";
printf("%s\n", str.c_str());
return true;
}
}
return false;
}
int Scan::addbta(string str)
{
for (int i = 0; i < bta.size(); i++)
{
if (str == bta[i])
return i;
}
//没找到,加入
bta.push_back(str);
return bta.size() - 1;
}
int Scan::addcta(string str)
{
for (int i = 0; i < cta.size(); i++)
{
if (str == cta[i])
return i;
}
cta.push_back(str);
return cta.size() - 1;
}
bool Scan::Isother(char a)
{
if (a >= 'A'&&a <= 'Z')
return false;
if (a >= 'a'&& a <= 'z')
return false;
if (a >= '0'&& a <= '9')
return false;
return true;
}
bool Scan::Isletter(char a)
{
if (a >= 'A' && a <= 'Z')
return true;
if (a >= 'a'&& a <='z')
return true;
return false;
}
bool Scan::Isnumber(char a)
{
if (a >= '0' && a <= '9')
return true;
return false;
}
Token * Scan::scanner(FILE * f) //用来返回一个Token,然后将其加入到序列中,直到不产生
{
char ch;
string str = "";//初始为空串
Token * t = NULL;
ch = fgetc(f);
if (ch != EOF)
{
while (ch == ' ' || ch == '\n' || ch == '\t')
{
ch = fgetc(f);
}//去掉所有的空格
//根据第一个字母判断各种状态
if (Isletter(ch)) //如果第一个字母为英文字母
{
cout << "进入标识符状态" << endl;
str = str + ch;
ch = fgetc(f);
while (Isletter(ch) || Isnumber(ch))
{
cout << "再次进入" << endl;
str = str + ch;
ch = fgetc(f);
}
if (Isother(ch)) //再次读入的不是一个字符或者数字
{
cout << "判断为其他的字符串" << endl;
//将指针返回一个
ungetc(ch,f);
//判断是否为关键字
if (reservedLookup(str))
{
//如果是保留字,生成一个新的token
printf("%s\n", str.c_str());
t = new Token(str, -2);
return t;
}
else
{
//如果为标识符
int id = addbta(str);
printf("%s\n", str.c_str());
t = new Token("标识符", id);
return t;
}
}
}
if (Isnumber(ch))
{
cout << "进入数字状态" << endl;
//如果第一个字符为一个数字
str = str + ch;
ch = fgetc(f);
while (Isnumber(ch))
{
str = str + ch;
ch = fgetc(f);
}
if (Isother(ch))
{
ungetc(ch, f);
int id = addcta(str);
t = new Token("数字", id);
printf("%s\n", str.c_str());
return t;
}
}
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' ||
ch == ')' || ch == ';' || ch == '[' || ch == ']' || ch == '=')
{
cout << "进入运算符号状态" << endl;
str += ch;
t = new Token(str, -1);
printf("%s\n", str.c_str());
return t;
}
if(ch == ':')
{
cout << "进入:号状态" << endl;
str = str + ch;
ch = fgetc(f);
if (ch == '=')
{
str = str + ch;
t = new Token(str, -1);
printf("%s\n", str.c_str());
return t;
}
else
{
printf("非法符号 %c \n", ch);
error = 1;
return NULL;
}
}
if (ch == '{') //注释部分的处理
{
cout << "进入注释处理状态" << endl;
while (ch != '}')
{
ch = fgetc(f);
}
t = new Token("zhushi", -4);
return t;
}
if (ch == '.')
{
cout << "进入数组下届判断状态" << endl;
str = str + ch;
ch = fgetc(f);
if (ch == '.')
{
str = str + ch;
t = new Token("数组下限符", -1);
printf("%s\n", str.c_str());
return t;
}
else
{
return NULL;
}
}
if (ch == ',')
{
cout << "进入逗号判断状态" << endl;
str = str + ch;
ch = fgetc(f);
if (Isnumber(ch) || Isletter(ch))
{
ungetc(ch, f);
t = new Token(str, -1);
printf("%s\n", str.c_str());
return t;
}
else
{
str = str + ch;
printf("非法符号 %s \n", str);
error = 1;
return NULL;
}
}
printf("非法符号 %c \n", ch);
error = 1;
return NULL;
}
else
{
return NULL;
}
}
int main(void)
{
FILE *fp;
if ((fp = fopen("D:\\ce.txt", "r")) == NULL)
{
printf("文件打开失败\n");
exit(1);
}
/*char a;
a = fgetc(fp);
printf("%c\n", a);
cout << "ceshi shifouleyi " << endl;*/
Scan * scan = new Scan();
vectort;//本容器用来存储所有的Token序列
//申请第一个节点
Token *token = NULL;
while ((token = scan->scanner(fp))!= NULL)
{
t.push_back(*token);
token = NULL;
}
//关闭文件
fclose(fp);
//将得到的list中的内容写到一个新的文件中,这个文件由程序生成。需要调用函数
int i = 0;
//判断是是否有错误
if (error == 1)
{
cout << "有错误" << endl;
}
else
{
//第一步打印标识符表
printf("打印标识符表\n");
for (i; i < bta.size(); i++)
{
printf("%d---%s\n", i, bta[i].c_str());
}
printf("打印数字表\n");
for (i = 0; i < cta.size(); i++)
{
printf("%d---%s\n", i, cta[i].c_str());
}
printf("打印所有的Token序列\n");
for (i = 0; i < t.size(); i++)
{
printf("%d---%s---%d\n", i, t[i].type.c_str(), t[i].id);
}
}
//将所得到的及结果放到一个文件中进行存储
//这里需要进行文件的写操作
FILE *fw;
if ((fw = fopen("D:\\jg.txt", "w")) == NULL)
{
printf("文件打开失败\n");
exit(1);
}
else
{
for (i = 0; i < t.size(); i++)
{
if (t[i].type != "zhushi")
{
fprintf(fw, "[%s]---[%d]\n", t[i].type.c_str(), t[i].id);
}
}
}
fclose(fw);
system("pause");
return 0;
}
在编写程序过程中遇到的问题:
1.C语言中string类型的输出:
在采用printf或者cout<<进行输出的时候都会出现乱码的现象,但是利用字符串调用.c_str()函数该现象就会消失
原因:printf函数是针对char*的,故要正确显示,通过c_str()返回首地址。
2)VS2017在使用文件打开或者关闭的时候会出现unsafe错误
处理办法:右键工程名----->属性---->C/C++----->预处理器------>预处理器定义---->在右边的输入框中加入_CRT_SECURE_NO_WARNINGS 然后点击保存。
3)C++中可以使用流的方式读取文件的内容,以getline函数读内容,举例如下:
#include
char buffer[256];
fstream ex("D:\\ce.txt");//注意这个地方一定要是两个\\,否则是识别不出来的
if (!ex.is_open())
{
cout << "文件打开失败" << endl;
exit(0);
}
while (!ex.eof())
{
ex.getline(buffer, 13);
cout << buffer << endl;
}
4.字符串可以利用+号直接在后面追加 ,或者可以利用append函数(该函数也是将字符加入当当前字串的尾部)
本文章参考资料:https://blog.csdn.net/kyt511/article/details/46490845
https://www.cnblogs.com/zyrblog/p/6885922.html