• 数据结构课程设计——程序分析


    4题 程序分析

    班级:-------------  姓名:------  学号:-------------------------------------   完成日期:----------

    【问题描述】

    读入一个C程序,统计程序中的代码、注释、和空行的行数以及函数的个数和平均行数,并利用统计信息分析评价该程序的风格。

    【基本要求】

    1. 把C程序文件按字符顺序读入源程序;
    2. 边读入程序、边识别统计代码行、注释行、空行,同时还要识别函数的开始和结束,以便统计其个数和平均行数;
    3. 程序的风格评分分为代码、注释和空行三个方面、每个方面分为A、B、C和D四个等级,划分标准为:

     

    A级

    B级

    C级

    D级

    代码(函数平均长度)

    10~15行

    8~9或16~20行

    8~9或16~20行

    8~9或16~20行

    注释(占总行比率)

    15~25%

    10~14或26~30%

    5~9或31~35%

    <5%或>35%

    空行(占总行比率)

    15~25%

    10~14或26~30%

    5~9或31~35%

    <5%或>35%

     

    【测试数据】

    先对较小的程序进行分析。当年的程序能正确运行时,对你的程序本身进行分析。

    【实现提示】

    为了实现的方便,可做以下的约定:

    1. 头两个字符是“//”的行称为注释行(该行不含语句)。除了空行和注释行外,其余均为代码行(包括类型定义、变量定义和函数头)。
    2. 每个函数代码的行数(除去空格和注释行)称为该函数的长度。
    3. 每行最多只有一个“{”、“}”、“switch”和“struct”(便于识别函数的结束行)。

    【选作内容】

    1. 报告函数的平均长度。
    2. 找出最长函数及其在程序中的位置。
    3. 运行函数的嵌套定义,报告最大的函数嵌套深度。
    4.  

    代码:

    #include<iostream>
    #include<string>
    #include<fstream>
    #include<regex> 
    #include<iomanip>
    #include<queue>
    #include<Windows.h>
    #define max 10000
    //正则表达式头文件
    using namespace std;
    typedef struct node;
    typedef node *tree;
    int graph[max][max];	//函数嵌套定义邻接矩阵
    int num[max];
    int visited[max];
    //打开文件名
    string name ("test.cpp");   
    //括号匹配
    int  kuohao = 0;
    //函数长度计数开关
    int flag = 0;
    //函数行数
    int fun = 0;	
    //代码总行数
    int line = 0;	
    
    struct result
    {	//代码行
    	int Code = 0;			
    	//注释行
    	int Comments = 0;			
    	//空行
    	int Blanklines = 0;			
    	
    	int scode = 0;
    	int scomment = 0;
    	int sspace = 0;
    	//函数个数
    	int countfun = 0;		
    	//函数总长度
    	int funlen = 0;		
    	//最长函数
    	int maxlen = 0;
    	//函数平均长度
    	double avelen = 0;
    	//最长函数所在行数
    	int maxline = 0;
    	//记录函数名
    	int maxh = 0;
    	string funname[max];
    	//最长函数名
    	string maxfun;
    	
    }list;
    /*根据获取到的数据,对程序进行评分*/
    string evaluate(result l) 
    {
    	string value;
    	if (l.avelen >= 10 && l.avelen <= 15)
    		value += 'A';
    	else if ((l.avelen >= 8 && l.avelen < 10) || (l.avelen > 15 && l.avelen <= 20))
    		value += 'B';
    	else if ((l.avelen >= 5 && l.avelen < 8) || (l.avelen > 20 && l.avelen <= 24))
    		value += 'C';
    	else
    		value += 'D';
    	if (list.scomment >= 15 && list.scomment <= 25)
    		value += 'A';
    	else if ((list.scomment >= 10 && list.scomment < 15) || (list.scomment > 25 && list.scomment <= 30))
    		value += 'B';
    	else if ((list.scomment >= 5 && list.scomment < 10) || (list.scomment > 30 && list.scomment <= 35))
    		value += 'C';
    	else
    		value += 'D';
    	if (list.sspace >= 15 && list.sspace <= 25)
    		value += 'A';
    	else if ((list.sspace >= 10 && list.sspace < 15) || (list.sspace > 25 && list.sspace <= 30))
    		value += 'B';
    	else if ((list.sspace >= 5 && list.sspace < 10) || (list.sspace > 30 && list.sspace <= 35))
    		value += 'C';
    	else
    		value += 'D';
    	return value;
    }
    /*根据成绩给出相印的评语*/
    string remark(char s)
    {
    	if (s=='A')
    		return "Excellent";
    	else if (s == 'B')
    		return "good";
    	else if (s == 'C')
    		return  "common";
    	else
    		return "bad";
    }
    string off(string s)
    {
    	string cs;
    	int open = 0;
    	for (int i = 0;; i++) {
    		if (s[i] == '('&&open == 1) {
    			return cs;
    		}
    		if (open)
    			cs += s[i];
    		if (s[i] == ' '&&open==0) {
    			open = 1;
    		}
    	}
    }
    void analyze(string s) 
    {
    	//匹配到函数定义
    	if (regex_match(s, regex("^(\w{1,10}) ([qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_]{1,100})([qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_0123456789]{0,100})\((.{0,100})\).{0,2}"))) {    
    		//函数个数+1
    		list.countfun++;	
    		
    		list.funname[list.countfun] = s;
    		//开启计数器
    		flag = 1;
    	
    	}
    	if (regex_search(s, regex("\{"))) 
    	{
    		kuohao++;
    	}
    	if (regex_search(s, regex("\}"))) 
    	{
    		if (kuohao)
    			kuohao--;
    		if(kuohao==0&&flag)
    			{
    			fun++;   
    			//加上尾花括号
    			list.funlen++;
    			if (fun > list.maxlen) 
    			{
    				list.maxlen = fun;
    				list.maxfun = list.funname[list.countfun];
    				list.maxline = line-fun+1;
    			}
    			fun = 0;
    			flag = 0;
    		}
    	}
    	if (regex_search(s, regex("//")))  
    		//注释行搜索;
    		list.Comments++;
    	else if (s == "")					
    		//空行搜索
    		list.Blanklines++;
    	else {
    		if (flag)
    		{
    			for (int i = 1; i < list.countfun; i++) {
    				if (regex_search(s,regex(off(list.funname[i]))) != NULL) {
    					graph[list.countfun][++num[list.countfun]] = i;
    				}
    			}		
    			list.funlen++;
    		}
    		list.Code++;
    	}
    	if (flag) {
    		fun++;
    	}
    }
    /*通过深度优先遍历寻找函数的最大深度*/
    void dfs(int i) {
    	static int count;
    	visited[i] = 1;
    	for (int j = 1; j <= num[i]; j++) {
    		if (!visited[graph[i][j]]) {
    			count++;
    			dfs(graph[i][j]);
    			
    		}
    	}
    	if (!num[i]) {
    		if (count+1>list.maxh) {
    			list.maxh = count + 1;
    		}
    		count = 0;
    	}
    }
    /*展示面板*/
    void show() 
    {	
    	//计算每个函数的最大镶嵌深度
    	for (int i = 1; i <= list.countfun; i++) {
    		dfs(i);
    		memset(visited, 0, sizeof(visited));
    	}
    	list.scode = (int)((double)list.Code / (list.Code + list.Comments + list.Blanklines)*100+0.5);
    	list.scomment = (int)((double)list.Comments / (list.Code + list.Comments + list.Blanklines) * 100 + 0.5);
    	list.sspace = 100 - list.scode - list.scomment;
    	list.avelen = (double)list.funlen / list.countfun;
    	string res = evaluate(list);
    	cout << "The result of analysing program file "" + name + "":" << endl;
    	cout << "    Lines of code :      " << list.Code << endl;
    	cout << "    Lines of comments :  " << list.Comments << endl;
    	cout << "    Blank lines :        " << list.Blanklines << endl;
    	cout << "    Code    Comments    Space" << endl;
    	cout << "    ====    ========    =====" << endl;
    	cout << "     " <<list.scode<< "%   "<<setw(6)<<list.scomment << "%        " << list.sspace<<"%"<< endl;
    	cout << "    The program include " << list.countfun << " functions." << endl;
    	cout << "    The average length of a section of code is " <<list.avelen<< " lines." << endl;
    	cout << "    The longest function is " << list.maxfun << ":" << list.maxlen << " lines." << endl;
    	cout << "    The longest function is " <<"on the " <<list.maxline<<" line"<< endl;	
    	cout << "    The maximum function nesting is :" << list.maxh << endl;
    	cout << "    Grade " << res[0] <<" "<< remark(res[0]) << " routine size style." << endl;
    	cout << "    Grade " << res[1] <<" "<< remark(res[1]) << " commenting style." << endl;
    	cout << "    Grade " << res[2] <<" "<< remark(res[2]) << " white space style." << endl;
    }
    int main()
    {	
    	int key;
    	cout << "---------------------------------------------------------------" << endl;
    	cout << "    请输入进行分析的程序文件路径及其后缀(可使用相对路径)" << endl;
    	cout << "---------------------------------------------------------------" << endl;
    	cout << "分析文件路径:";
    	getline(cin, name);
    	ifstream in(name);
    	//读入文件
    	system("cls");
    	if (!in) 
    	{
    		cout << "----------------------------------------------------------" << endl;
    		cout << "                     文件打开失败                         " << endl;
    		cout << "----------------------------------------------------------" << endl;
    		Sleep(2000);
    		system("cls");
    		main();
    	}
    	string str;
    	while (1) 
    	{
    		line++;
    		if (in.eof())			
    			//判断文件读取结束
    			break;
    		getline(in, str);	
    		analyze(str);
    	}
    	in.close();
    	show();
    	cout << "----------------------------------------------------------" << endl;
    	cout << "               1.分析其他程序   2.退出                    " << endl;
    	cout << "----------------------------------------------------------" << endl;
    	cout << "请输入序号 : ";
    	cin >> key;
    	if (key == 1) {
    		system("cls");	//清屏
    		getchar();	  //清除缓存
    		main();
    	}
    	return 0;
    }

    分析文档 :

    二、概要设计
    1、定义输出表单数据结构体
    Struct result{
    int Code = 0;
    	//注释行
    	int Comments = 0;
    	//空行
    	int Blanklines = 0;
    	int scode = 0;
    	int scomment = 0;
    	int sspace = 0;
    	//函数个数
    	int countfun = 0;
    	//函数总长度
    	int funlen = 0;
    	//最长函数
    	int maxlen = 0;
    	//函数平均长度
    	double avelen = 0;
    	//最长函数所在行数
    	int maxline = 0;
    	//记录函数名
    	int maxh = 0;
    	string funname[max];
    	//最长函数名
    	string maxfun;
    }
    2、实现概要
    (1)主函数部分:
    Int main()
    {
    读取C程序;
    分析数据;
    输出表单。
    }
    
    (2)分析部分:
    Void analyze(读取数据)
    {
    If(匹配注释行)
    注释行计数
    Else if(匹配空行)
    空行计数
    Else
    代码行计数
    If(匹配函数定义)
    {
    函数个数+1
    开启函数行计数功能
    记录函数名
    }
    If(匹配函数结尾)
    {
    关闭函数行技术
    寻找最长函数
    }
    If(函数行计数开关)
    {
    函数行进行计数;
    在该函数中寻找子函数,填入邻接表(数组模拟)
    }
    }
    (3)展示部分:
    Void show(读取结构体数据){
    对结构体中数据进行分析。
    进行评分。
    按照题目要求输出程序分析结果。
    }
    
    三、详细设计
    1.读入C程序:
    Ifstream in(C程序相对路径);
    if (!in) 
    	{
    		cout << "文件打开失败" << endl;
    		return 1;
    	}
    	string str;			//采用string类存储字符串,便于之后操作
    	while (1) 
    	{
    		line++;
    		if (in.eof())			//判断文件读取结束
    			break;
    		getline(in, str);			//逐行读取
    		analyze(str);		//进行数据分析
    	}
    2.数据分析:
    void analyze(string s) 
    {
    	if (regex_match(s, regex("^(\w{2,10}) (\w{1,100})\((.{0,100})\).{0,2}"))) {    
    		//通过正则表达式匹配函数定义
    		list.countfun++;	
    		//函数个数+1
    		list.funname[list.countfun] = s;
    		flag = 1;
    		//开启计数器
    	}
    	if (regex_search(s, regex("\{"))) 	//通过变量kuohao模拟栈来进行括号匹配
    	{
    		kuohao++;
    	}
    	if (regex_search(s, regex("\}"))) 
    	{
    		if (kuohao)
    			kuohao--;
    		if(kuohao==0&&flag)	//当栈空则说明函数结束
    			{
    			fun++;   
    			//加上尾花括号
    			list.funlen++;
    			if (fun > list.maxlen) 		//与之前的最长函数比较得到新的最长函数
    			{
    				list.maxlen = fun;		//存储长度
    				list.maxfun = list.funname[list.countfun];		//存储最长函数名
    				list.maxline = line-fun+1;			//记录最长行数所在行
    			}
    			fun = 0;			//函数行数计数器清零
    			flag = 0;			//关闭函数行计数功能
    		}
    	}
    	if (regex_search(s, regex("//")))  //注释行搜索;
    		list.Comments++;
    	else if (s == "")					//空行搜索
    		list.Blanklines++;
    	else {
    		if (flag)	//函数行处理
    		{				//在当前函数中找子函数建立邻接表
    			for (int i = 1; i < list.countfun; i++) {
    				if (strstr(s.c_str(), off(list.funname[i]).c_str()) != NULL) {
    					graph[list.countfun][++num[list.countfun]] = i;	//子函数匹配
    				}
    			}
    			fun++;
    			list.funlen++;
    		}
    		list.Code++;		//代码行计数
    	}
    }
    3.输出部分:(按照题目版面进行输出)
    void show() 
    {	
    	//计算每个函数的最大镶嵌深度
    	for (int i = 1; i <= list.countfun; i++) {
    		dfs(i);
    		memset(visited, 0, sizeof(visited));
    	}
    	list.scode = (int)((double)list.Code / (list.Code + list.Comments + list.Blanklines)*100+0.5);
    	list.scomment = (int)((double)list.Comments / (list.Code + list.Comments + list.Blanklines) * 100 + 0.5);
    	list.sspace = (int)((double)list.Blanklines / (list.Code + list.Comments + list.Blanklines) * 100 + 0.5);
    	list.avelen = (double)list.funlen / list.countfun;
    	string res = evaluate(list);
    	cout << "The result of analysing program file "" + name + "":" << endl;
    	cout << "    Lines of code :      " << list.Code << endl;
    	cout << "    Lines of comments :  " << list.Comments << endl;
    	cout << "    Blank lines :        " << list.Blanklines << endl;
    	cout << "    Code    Comments    Space" << endl;
    	cout << "    ====    ========    =====" << endl;
    	cout << "     " <<list.scode<< "%   "<<setw(6)<<list.scomment << "%        " << list.sspace<<"%"<< endl;
    	cout << "    The program include " << list.countfun << " functions." << endl;
    	cout << "    The average length of a section of code is " <<list.avelen<< " lines." << endl;
    	cout << "    The longest function is " << list.maxfun << ":" << list.maxlen << " lines." << endl;
    	cout << "    The longest function is " <<"on the " <<list.maxline<<"th line"<< endl;	
    	cout << "    The maximum function nesting is :" << list.maxh << endl;
    	cout << "    Grade " << res[0] <<" "<< remark(res[0]) << " routine size style." << endl;
    	cout << "    Grade " << res[1] <<" "<< remark(res[1]) << " commenting style." << endl;
    	cout << "    Grade " << res[2] <<" "<< remark(res[2]) << " white space style." << endl;
    }
    4.选作完成部分
    (1)计算函数的平均
    答:开启flag计算函数部分,利用count记录flag开启次数,即函数个数,故
    平均长度(avelen)=函数总长度(funlen) / 函数个数(count);
    (2)找出最长函数及位置
    答:增设两个变量记录最长函数名及其位置,每次关闭flag(即函数结尾),与这两个变量进行比较替换。
    (3)找出最大函数镶嵌深度
    答:增设一个变量(maxh)表示最大深度,在开启flag的时候,同时寻找子函数,建立邻接表(数组模拟),然后根据深度优先遍历,配合静态变量,最后与maxh进行比较替换。
    附:深度优先遍历部分:
    void dfs(int i) {
    	static int count;
    	visited[i] = 1;
    	for (int j = 1; j <= num[i]; j++) {
    		if (!visited[graph[i][j]]) {
    			count++;
    			dfs(graph[i][j]);
    		}
    	}
    	if (!num[i]) {
    		if (count+1>list.maxh) {
    			list.maxh = count + 1;
    		}
    		count = 0;
    	}
    }
    四、调试分析
    1、这道题目主要是对文件的读取分析操作,将文本的每一行用string存取,通过各自的计数器,记录程序分析的结果。
    2、函数的搜索,采用正则表达式进行匹配,搜索到函数定义,开启flag函数计数部分,
    通过简易栈((int)kuohao),进行花括号({})匹配,当栈空时函数结束。
    3、函数行、代码行和空行的比例采用四舍五入的方法。
    4、程序评分中,单独用一个函数(string evaluate)根据数据进行评分后,返回3个字符,再用(string remark)函数对单独的字符给出评语。
    5、求函数镶嵌深度,主要使用树的思路,也可以算是有向图吧..,根据建立的邻接表,进行深度优先遍历,再寻找最大深度。
    五、用户手册
    1.本程序的运行环境为DOS操作系统,执行文件为:程序分析.exe。
    2.进入演示程序后,显示文本方式的用户界面:
    
    3.键入程序路径开始程序分析,路径不存在会提示文件打开失败,暂停2s后重新执行主程序。
    4.进行分析后输入操作指令可继续分析其他程序。
    六、测试结果。
    1.分析本程序(main.cpp)
    
    2.分析测试程序(test.cpp)
    
    七、附录
    程序头文件:
    #include<iostream>
    #include<string>
    #include<fstream> //文件读取,输出函数
    #include<regex> //正则表达式头文件
    #include<iomanip> 
    #include<queue>  //STL库中队列
    #define max 10000   //给定最大值
    using namespace std;
  • 相关阅读:
    vue element-admin 清空校验
    vue+elementui 动态改变表单必填项
    什么是中台
    项目中遇到的一道算法题
    【解决】Word中公式突然乱码
    【解决】MATLAB报错:此上下文中不支持函数定义,请在代码文件中创建函数。
    【解决】Word打印成PDF出错:%%[ ProductName: Distiller ]%%
    Bike Sharing Analysis(二)- 假设检验方法
    Bike Sharing Analysis(一)- 探索数据
    Spark Structured Streaming(二)实战
  • 原文地址:https://www.cnblogs.com/F-itachi/p/9974364.html
Copyright © 2020-2023  润新知