词法分析器--C实现

article/2025/11/1 10:24:17

实验目的:

编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类(可自主添加类别)。并依次输出各个单词的内部编码及单词符号自身值。

 程序及其子程序:

1、文件输入(待分析文本拖入控制台或屏幕输入)

2、划分字符集成7个大类

3、预处理(去除空格、回车换行和非法字符)

4、词法分析

5、打印输出识别的终结符及其类别

//程序无法识别出double,因为它的前缀子串与do重复(如有需要自主修改)
/*
编译原理实验一:词法分析器
要求:编制一个读单词过程,从输入的C语言源程序中,识别出各个具有独立意义的单词,
即基本保留字、标识符、常数、运算符、分隔符、特殊字符、控制命令七大类。
并依次输出各个单词的内部编码及单词符号自身值的二元组。
*//*
一、复习c语言字符串数组相关处理,文件相关处理//用字符数组存放一个字符串
char str[ ]="";
//用字符指针指向一个字符串
char * s="I love China";
//二维数组存放字符串数组
const int N = 5;
char frd[N][10]={"Hello","World","My","Dear","Friends"};
//字符指针的数组存放字符串数组
char *p[]={"This","is","a","char*","array"};
//文件读写函数原型:FILE *fopen(const char *filename, const char *mode);`头文件:#include <stdio.h>`二、词法分析器
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#define MAX_LINE 1024*10//编码,保留字:1、标识符:2、常数:3、运算符:4、分隔符: 5//控制命令表
static char *contCommands[]={"#include","#define","#undef","#asm","#endasm","#ifdef","#ifndef","#else","#endif"};//关键字表
static char *keyWords[]={"main","int","double","struct","if","else","char","return","const","float",
"short","void","while","for","break","then","long","switch","case","do","static","typedef","continue",
"default","sizeof","do","extern","static","auto","register","sizeof"};//运算符表
static char *operators[]={"+","-","*","\/","=","%",">","<","^","&","|","!"};//分隔符表
static char *delimiters[]={",","(",")","{","}","[","]",";","\""};//特殊符号表
static char *Spesymbols[]={".","$","?","~","^","%","\\","#","&",":","`","@"};//科学计数法
// 数字
//const char* number_rule="^([+-]?\\d+\\.\\d+)|([+-]?\\d+)|([+-]?\\.\\d+)$";
//const std::regex pattern_number(number_rule, regex::icase);
//科学计数
//const char* scientific_rule="^[+-]?((\\d+\\.?\\d*)|(\\.\\d+))[Ee][+-]?\\d+$";
//const regex pattern_scientific(scientific_rule, regex::icase);
//十六进制
//const char* hex_rule="^[+-]?0[xX]([A-Fa-f0-9])+$";
//const regex pattern_hex(hex_rule, regex::icase);
//八进制
//const char* oct_rule="^0([0-7])+$";
//const regex pattern_oct(oct_rule, regex::icase);//输入的源程序存放处,最大可以存放MAX_LINE个字符
static char *resourceProject = (char*)malloc(MAX_LINE * sizeof(char));
//p = (int*)realloc(p, sizeof(int)* 20);扩容//存放注释
static char *commentStr = (char*)malloc(MAX_LINE * sizeof(char));
static char *commentStr2 = (char*)malloc(MAX_LINE * sizeof(char));//从str中删除目标字符
void delete_char(char str[],char target){int i,j;for(i=j=0;str[i]!='\0';i++){if(str[i]!=target){str[j++]=str[i];}}str[j]='\0';//i-j即为串中存在的目标字符个数
}//处理"//,/* */"注释
void proComment(char str[]){int j=0;int p=0;//遍历resourceProject,记录注释for(int i=0; i<strlen(str); i++) {if(str[i]=='/'&&str[i+1]=='/') {int k=i-1;while(str[++k]!='\n') {commentStr[p++]=str[k];}i=k;} }printf("\n单行注释为:%s\n",commentStr);//遍历resourceProject,原地删除单行注释j=0;p=0;int i;for(i=0; i<strlen(str); i++){if(str[i]=='/'&&str[i+1]=='/') {p=i;while(str[++p]!='\n');i=p;}else{str[j++]=str[i];}
}while(j<=i){str[j++]='\0';}printf("\n单行注释处理后的源程序为:\n%s",str);//处理多行注释“/* 。。。*/”则去除该内容int count = 0;p=0;int k=0;for (int i=0;i<strlen(str);i++){if(str[i]=='/'&&str[i+1]=='*'){k=i;while(str[k]!='*'||str[k+1]!='/'){commentStr2[p++]=str[k++];}commentStr2[p++]='*';commentStr2[p++]='/';i=k+2;}
//		 printf("\n多行注释处理后的源程序为:%s\n",str);}printf("\n多行注释为:%s\n",commentStr2);//遍历resourceProject,原地删除多行注释j=0;p=0;for(i=0;str[i]!='\0';i++){if(str[i]=='/'&&str[i+1]=='*'){i=i+2;p=i;while(str[p++]!='*'&&str[p+1]!='/');i=p+2;}else{str[j++]=str[i];}
}
printf("end\n");while(j<=i){str[j++]='\0';}printf("\n多行注释处理后的源程序为:\n%s",str);}//endproComment//预处理函数(可以写个target数组传进来)
void preProcessing(){//先处理注释,再处理宏定义等预处理,最后处理空格、换行、制表符proComment(resourceProject);char target1=' ';char target2='\t';char target3='\n';delete_char(resourceProject,target1);delete_char(resourceProject,target2);delete_char(resourceProject,target3);
}//判断是否为字母
bool isChar(char ch){if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))return true;return false;
}//判断是否为数字
bool isDigit(char ch){if(ch>='0'&&ch<='9')return true;return false;
}//判断是否为定界符等
bool isDelimiters(char ch){for(int i=0;i<sizeof(delimiters)/sizeof(delimiters[0]);i++)if(ch==*delimiters[i])return true;return false;
}//判断是否为控制命令
int isContCommands(char *str){for(int i=0;i<sizeof(contCommands)/sizeof(contCommands[0]);i++) {if(strcmp(str,contCommands[i])==0){//匹配return i;}}//不是关键字即为404return 404;
}//判断是否为关键字
int isKeyword(char *str){for(int i=0;i<sizeof(keyWords)/sizeof(keyWords[0]);i++) {if(strcmp(str,keyWords[i])==0){//匹配return i;}}//不是关键字即为404return 404;
}//判断是否为运算符
int isOperators(char str){for(int i=0; i<sizeof(operators)/sizeof(operators[0]); i++) {if(str==*operators[i]){return i;}}//不是关键字即为404return 404;
}//判断是否为特殊字符
int isSpesymbols(char str){for(int i=0; i<sizeof(Spesymbols)/sizeof(Spesymbols[0]); i++) {if(str==*Spesymbols[i]){return i;}}//不是关键字即为404return 404;
}FILE * readTxt(){FILE *fp;char filename[100];  //文件名char tempstr[1024];  //读文件的缓冲bool flag = true;   //文件读取成功标志printf("请输入或拖入想要打开的文本文件名及其路径,如c:\\temp.txt\n");while(flag){gets(filename);  //这句要用户输入文件名char target='"';delete_char(filename,target);//getchar();if ((fp=fopen(filename,"r"))==NULL){//打开文件,并判断是否有打开错误printf("打开文件%s出现错误\n",filename);memset(filename,0, sizeof filename);          //清空数组fclose(fp);  //关闭文件printf("请在检查文件路径后再次输入: \n");}else{flag = false;//文件读入resourceProjectint cnt=0;
//	 while(!feof(fp))   //读文件,直到文件末尾
//  	 {
//         resourceProject[cnt++] = fgetc(fp);  //将文件内容读入resourceProject
// 	 }printf("文件%s已读取,请检查以下txt文件内容:\n",filename);}}if (fp == NULL )return 0;//以下显示文件内容memset(resourceProject,0,sizeof resourceProject);while(fgets(tempstr,1024,fp)!=NULL) //读文件一行内容,最多1024字符到缓冲区,并判断是否文件已经结束{printf("%s",tempstr);   //显示缓冲区内容strcat(resourceProject,tempstr);//统一读入resourceProject}//    fclose(fp);  //关闭文件return  fp;
}//endRead//读入下一个字符
char getChar(int p,char str[])
{return str[p];
}//分析函数
char textAnalyze(char str[]){char* elements[strlen(str)];     //文本元素拆分int tag[strlen(str)];//1:关键字 2:标识符 3:常量 4:运算符 5:分隔符 6:特殊字符 7:控制命令memset(tag,'\0',sizeof(tag)/sizeof(tag[0]));int indTag=0;//tag的indexint Tag=0;memset(elements,'\0',sizeof elements);memset(elements,'\0',(sizeof tag)/sizeof tag[0]);char ch;//读入的字符int p=0;//str的指针char token[128]={};//记录变量名int m=0;
while((ch=getChar(p++,str))!='\0'){Tag=0;//printf("\ngetFirstChar:%c\n",ch);memset(token,'\0',sizeof(token)/sizeof(token[0]));if(isChar(ch)||ch=='_')  //可能是标示符或者关键字{m=0;token[m++]=ch;token[m]='\0';bool isKey=false;int tmp=p;bool While=false;while(isChar((ch=getChar(tmp++,str)))||isDigit((ch=getChar(--tmp,str)))){While=true;//printf("while:%c\n",ch);if(isDigit(ch))tmp++;token[m++]=ch;token[m]='\0';p++;if(isKeyword(token)!=404){tag[indTag++]=1;//关键字Tag=1;isKey=true;//	p--;break;}}//                token[m++]='\0';if(!isKey){//p++;tag[indTag++]=2;//标识符Tag=2;}//else//p++;//printf("(%d,\"%s\")\n",tag[indTag-1],token);printf("(%d,\"%s\")\n",Tag,token);}else if((ch=='-')||isDigit(ch)||(ch=='.'))//数字{bool sym=false;int m=0;char digit[128]={};memset(digit,'\0',sizeof(digit)/sizeof(digit[0]));digit[m++]=ch;digit[m]='\0';int tmp=p;if((ch=='-')&&isDigit((ch=getChar(tmp+1,str)))){sym=true;}long long constNum=0;while(isDigit(ch=getChar(tmp,str))||(ch=getChar(tmp,str))=='.'||(ch=getChar(tmp,str))=='e'||(ch=getChar(tmp,str))=='E'){digit[m++]=ch;digit[m]='\0';constNum=constNum*10+ch-'0';tmp++;p++;}digit[m++] = '\0';//结束符if(!sym){Tag=3;tag[indTag++]=3;//正数常量}else{tag[indTag++]=13;//负数常量Tag=13;}if(constNum>9223372036854775807){tag[indTag++]=23;//大数常量Tag=23;}printf("(%d,\"%s\")\n",tag[indTag-1],digit);}else if(isDelimiters(ch)){//	printf("%c\n",ch);token[0]=ch;token[1]='\0';tag[indTag++]=5;printf("(%d,\"%s\")\n",tag[indTag-1],token);}else if(isOperators(ch)!=404)//操作符{switch(ch)   //其他字符{case'<':m=0;token[m++]=ch;ch=getChar(p++,str);if(ch=='='){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{Tag=4;tag[indTag++]=4;token[m++]='\0';p--;printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'>':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='='){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;Tag=4;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'|':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='|'){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;Tag=4;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'&':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='&'){Tag=4;tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;Tag=4;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;case'!':m=0;token[m++]=ch;ch=getChar(p++,str);//取字符if(ch=='='){tag[indTag++]=4;token[m++]=ch;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}else{p--;tag[indTag++]=4;token[m++]='\0';printf("(%d,\"%s\")\n",tag[indTag-1],token);}break;default:{m=0;token[m++]=ch;token[m++]='\0';tag[indTag++]=4;printf("(%d,\"%s\")\n",tag[indTag-1],token);break;}}//endSwitch}//endIfElselse//特殊字符或控制命令{
//			printf("%c\n",ch);if(isSpesymbols(ch)!=404){//printf("%c\n",ch);bool isControl=false;if(ch=='#'){m=0;token[m++]=ch;token[m]='\0';int t=p;while(isChar((ch=getChar(t++,str)))){p++;token[m++]=ch;token[m]='\0';if(isContCommands(token)!=404){Tag=7;tag[indTag++]=7;//控制命令isControl=true;break;}}// p++;if(isControl){token[m++]='\0';
//        			printf("(%d,\"%s\")\n",tag[indTag-1],token);printf("(%d,\"%s\")\n",Tag,token);}}else{m=0;token[m++]=ch;token[m++]='\0';tag[indTag++]=6;//特殊符号符printf("(%d,\"%s\")\n",tag[indTag-1],token);}}}//endElse}//endWhile
}//endfunc//主函数
int main(){char buf[MAX_LINE];  /*缓冲区*/char ch,sh;FILE* fp;            /*文件指针*///下面是写数据,将数字0~9写入到data.txt文件中printf("***请选择读取待编译程序的方式***\n");printf("***输入 1:屏幕输入;输入 2:已放入记事本***\n");int tmp;scanf("%d",&tmp);getchar();//吞掉空格if (tmp==1){FILE *fpWrite=fopen("data.txt","a");if(fpWrite==NULL){perror("fail to read");exit(1);return 0;}printf("您输入的是 1,请继续输入待编译程序,并以@加回车结束!\n");printf("输入内容将同步显示..\n");ch = getchar();while (ch != '@') {fputc(ch,fpWrite); //写入文件putchar(ch); //输出到屏幕ch = getchar();}fclose(fpWrite);//关闭文件printf("程序已读入文件data.txt..");//已经获取文件处理程序//fpWrite=fopen("data.txt","a");//重新打开文件,重置文件指针//fclose(fpWrite);//关闭文件getchar();fp=readTxt();//获取文件指针printf("文件读取完毕..\n");printf("resourceProject中的程序为:\n");printf("%s",resourceProject);fclose(fp);//关闭文件}else if(tmp==2){printf("您输入的是 2,接下来将进入文件读取程序..\n");fp=readTxt();//获取文件指针printf("文件读取完毕..\n");printf("resourceProject中的程序为:\n");printf("%s",resourceProject);fclose(fp);//关闭文件}//开始处理读入的程序preProcessing();//预处理printf("\n预处处理完成后的程序为:\n");printf("%s",resourceProject);printf("\n下面开始词法分析:\n");textAnalyze(resourceProject);//下面是读数据,将读到的数据存到数组a[10]中,并且打印到控制台上
//    int a[10]={0};
//    FILE *fpRead=fopen("data.txt","r");
//    if(fpRead==NULL)
//    {
//        return 0;
//    }
//    for(int i=0;i<10;i++)
//    {
//        fscanf(fpRead,"%d ",&a[i]);
//        printf("%d ",a[i]);
//    }
//    getchar();//等待
//
//	 fclose(fpRead);free(commentStr);
free(resourceProject);return 0;
}

运行结果:

 

        对编译原理中的词法分析实验进行了记录,但需要说明的是,本次实验面向测试集编程,处理方式分为两遍,并非一遍扫描处理。

        编写代码时需十分小心指针的位置,符号的提前读入、回溯等。

        程序逻辑划分较为明显,有需要的可主要对进行词法分析的 char textAnalyze(char str[]) 函数进行改写。


http://chatgpt.dhexx.cn/article/tleeSgWI.shtml

相关文章

c语言实现词法分析器

词法分析器的功能:输入源程序&#xff0c;输出单词字符。单词字符一般可以分为下面五种。 &#xff08;1&#xff09;关键字 是由程序语言定义的具有固定意义的标识符。有时称这些标识符为保留字或者基本字。例如c语言中的int,char,define,strcut,double,if,else.等等 &#xf…

词法分析器(分析C语言)

问题描述&#xff1a; 用C或C语言编写一个简单的词法分析程序&#xff0c;扫描C语言小子集的源程序&#xff0c;根据给定的词法规则&#xff0c;识别单词&#xff0c;填写相应的表。如果产生词法错误&#xff0c;则显示错误信息、位置&#xff0c;并试图从错误中恢复。简单的恢…

词法分析器(c++)

前景提示&#xff1a; 个人觉得单纯是用来完成实验报告的话还行&#xff0c;但仅做参考&#xff0c;因为本人的编程水平有限&#xff0c;怕误人子弟。 本次代码支持以下操作&#xff1a; 单行注释 多行注释 文件形式输入 种别码可以在文件中自由修改 单词字符串识别支持…

词法分析——词法分析器的作用

目录 综述 正文 1 词法分析与语法分析 2 词法单元、模式和词素 3 词法单元的属性 4 词法错误 综述 词法分析是编译的第一阶段。词法分析器的主要作用是读入源程序的输入字符、将它们组成词素&#xff0c;生成并输出一个词法单元序列&#xff0c;每个词法单元对应一个词素。…

词法分析器

词法分析&#xff08;Lexical Analysis&#xff09; 词法分析器在英文中一般叫做 Tokenizer。 有一个计算模型&#xff0c;叫做有限自动机&#xff08;Finite-state Automaton&#xff0c;FSA&#xff09;&#xff0c;或者叫做有限状态自动机&#xff08;Finite-state Machin…

编译原理——词法分析器

1 概述 设计、编制并调试一个简单的C语言词法分析程序&#xff0c;掌握利用状态转换图设计词法分析器的基本方法&#xff0c;利用该词法分析器完成对源程序字符串的词法分析。通过对该词法分析器的设计&#xff0c;加深对词法分析原理、状态转换图等编译原理知识的理解。 2 使…

编译原理词法分析器(C/C++)

前言&思路 词法分析器不用多说&#xff0c;一开始我还不知道是什么样的&#xff0c;看了下别人的博客&#xff0c;再看看书&#xff0c;原来是输出二元组&#xff0c;这不就是字符串操作嘛。然后细看几篇博客&#xff0c;发现大都是用暴力判断来写的。我对代码重复性比较高…

【编译原理】词法分析(C/C++源代码+实验报告)

文章目录 1 实验目的和内容1.1实验目的1.2实验内容 2 设计思想2.1单词种类及其正规式2.2 根据正规式构造NFA2.3根据NFA构造DFA2.3.1根据替换规则构造未化简的DFA2.3.2最小化DFA 3算法流程4源程序5调试数据5.1 测试样例一5.2 测试样例二5.3 测试样例三 6实验调试情况及体会6.1 实…

session 每次请求都会产生新的sessionID

问题描述&#xff1a; 最近在写一个项目时&#xff0c;在运行项目后每刷新一次都会产生一个新的Session ID&#xff0c;导致无法取值。 原因分析&#xff1a; 搞了很久发现是URL路径的问题&#xff0c;把http://localhost:8080//的双斜杠该为单斜杠就行了 解决方案&#xf…

JavaWeb - Cookie、Session、SessionId 详解

一、概述 会话&#xff08;Session&#xff09;跟踪是Web程序中常用的技术&#xff0c;用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份&#xff0c;Session通过在服务器端记录信息确定用户身份。 本章将系统地讲述Co…

JSESSIONID和sessionid的区别

要保持登陆状态&#xff0c;但是sessionid 和 JSESSIONID的值不一致&#xff0c; 情况一&#xff1a;部署到测试机上&#xff0c;利用本机登陆网页&#xff0c;sessionid和jsessionid不一样。 情况二&#xff1a;部署在本机&#xff0c;本机登陆页面&#xff0c;sessionid和js…

关于两次访问接口的sessionid不一致问题

在测试验证邮箱、注册逻辑时&#xff0c;出现验证码错误的问题。验证码是存放在session内的&#xff0c;在排除了逻辑代码的问题后&#xff0c;检查出这两次访问接口的sessionid并不一致&#xff0c;而在swagger测试接口时是一致的。因此我比较了swagger与ajax请求/响应头的区别…

cookie、session、sessionid 与jsessionid

cookie、session、sessionid 与jsessionid&#xff0c;要想明白他们之间的关系&#xff0c;下面来看个有趣的场景来帮你理解。 我们都知道银行&#xff0c;银行的收柜台每天要接待客户存款/取款业务&#xff0c;可以有几种方案&#xff1a; 凭借柜台职员的记忆&#xff0c;由收…

如何根据sessionID获取session解决方案

点个赞&#xff0c;看一看&#xff0c;好习惯&#xff01;本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录&#xff0c;这是我花了3个月总结的一线大厂Java面试总结&#xff0c;本人已拿腾讯等大厂offer。 另外&#xff0c;原创文章首发在我的个人博客&#…

sessionId的生成过程和过期时间

支持作者 最便宜的卫生纸 浏览器第一次请求服务器时&#xff0c;服务器会生成一个sessionId&#xff0c;并返回给浏览器&#xff0c;这个sessionId会被保存在浏览器的会话cookie中。如下图 在浏览器不关闭的情况下&#xff0c;之后的每次请求请求头都会携带这个sessionId到服务…

session,sessionid,cookie之间的关系解析

session&#xff0c;sessionid&#xff0c;cookie之间的关系解析 文章目录 session&#xff0c;sessionid&#xff0c;cookie之间的关系解析1.简介2.session和cookie定义&#xff0c;创建&#xff0c;周期和联系2.1cookie2.2session 3.如果禁用cookie后&#xff0c;如何解决账号…

dubbox拦截器配置

dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架&#xff0c;即使从国际视野来看应该也是一个非常全面的SOA基础框架。作为一个重要的技术研究课题&#xff0c;在当当网我们根据自身的需求&#xff0c;为Dubbo实现了一些新的功能&#xff0c;并将其命名为Dubbox&…

Springboot+Dubbox 提供Rest服务实践

背景 在开发过程中&#xff0c;dubbo接口自测时&#xff0c;通过控制台的invoke方式调用dubbo服务不方便&#xff0c;主要体现在入参设置和入参保存上&#xff08;invoke方式调用dubbo服务请参考&#xff1a;命令行中调用dubbo服务及入参写法_Ypc_victor的专栏-CSDN博客&#…

Dubbo2

一、基础知识 1、分布式基础理论 1.1&#xff09;、什么是分布式系统&#xff1f; 《分布式系统原理与范型》定义&#xff1a; “分布式系统是若干独立计算机的集合&#xff0c;这些计算机对于用户来说就像单个相关系统” 分布式系统&#xff08;distributed system&#…

Dubbox 是什么?

1. Dubbo是什么&#xff1f; Dubbo是一个分布式服务框架&#xff0c;致力于提供高性能和透明化的RPC远程服务调用方案&#xff0c;以及SOA服务治理方案。简单的说&#xff0c;dubbo就是个服务框架&#xff0c;如果没有分布式的需求&#xff0c;其实是不需要用的&#xff0c;只…