练涛

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;

 

posted on 2018-08-26 14:47  氵丨  阅读(740)  评论(0编辑  收藏  举报