2021秋软工实践第一次个人编程作业

HorizonXRR 2021-09-20 16:23:34

2021秋软工实践第一次个人编程作业

这个作业属于哪个课程构建之法-2021秋-福州大学软件工程
这个作业要求在哪里2021秋软工实践第一次个人编程作业
这个作业的目标实现一个程序功能,它可以对读入的C或C++代码文件进行不同等级的关键字提取。
学号031902124

代码仓库

Github个人仓库

代码规范

代码规范参照Google开源项目风格指南(C++)

已上传至github仓库

代码规范

PSP表格

 

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划  
Estimate估计这个任务需要多少时间3030
Development开发  
Analysis需求分析 (包括学习新技术)120150
Design Spec生成设计文档3030
Design Review设计复审1010
Coding Standard代码规范 (为目前的开发制定合适的规范)5050
Design具体设计6060
Coding具体编码180300
Code Review代码复审3030
Test测试(自我测试,修改代码,提交修改)120180
Reporting报告  
Test Report测试报告2030
Size Measurement计算工作量2020
Postmortem & Process Improvement Plan事后总结, 并提出过程改进计划3030
 合计700920

在饼图中我们可以看到,Coding的实际占比要比预估中多不少,以及代码测试也花了不少时间。

解题

 

问题理解

拿到题目后第一件事情就是先理解问题。这一次作业的要求是实现一个程序功能,它可以对读入的C或C++代码文件进行不同等级的关键字提取。我们可以把这个题目抽象成为一个字符串类型的问题。字符串类型的问题通常可以通过调用各种方法对其进行筛选,找到自己想要的信息。但是这一次的作业有一些不同,就是作为代码文件,是会存在嵌套的。这次编程选择的语言是C++,毕竟还是比较熟悉这个。

资料查询

知道了自己要干什么之后我就开始着手查询资料,让我没想到的是居然在查询怎么样读取文件时花了一点时间。以前对于文件流的输入输出这块还是不够熟悉啊。然后就开始查询字符串处理的方法。STL中的string自带一些方法,并且为了更有效率地统计关键词各自出现的频率,我用到了map中的一些方法,最后再加和进行统计。

整体设计

 

关键代码阐释


不同字符的处理

    for (int i = 0; i < save.length(); i++) {
        cut_num = MyCut(save[i]);
        if (i + 1 <= save.length() && save[i] == '/' && save[i + 1] == '/') {
            //判断注释的情况
            int j;
            for (j = i + 2; j < save.length() && save[j] != '\n'; j++);
            i = j;
        }
        else if (save[i] == '\"') {
            int j;
            for (j = i + 1; j < save.length() && save[j] != '\"'; j++);
            i = j;
        }
        else if (cut_num == 1) {
            key_guan.push_back(save[i]);
        }
        else if (cut_num == 2) {
            string s(1, save[i]);
            unfiltered_key.push_back(s);
        }
        else {
            if (key_guan.length() > 1) {
                if (map_key_guan.find(key_guan) != map_key_guan.end()) {
                    map_key_guan[key_guan] += 1;
                    unfiltered_key.push_back(key_guan);
                    total_key_counter++;
                }
            }
            key_guan.clear();
        }
    }

通过条件判断来对不同的字符类型进行划分,对引号内字符串以及注释进行了特殊处理,最后一个条件判断一下是否符合关键词存入的条件,如果符合就加入关键词组中。


嵌套等级的判断

    int level = 0;//嵌套等级,初始为0
    pair<int, string> temp;
    for (int i = 0; i < unfiltered_key.size(); i++) {
        if (unfiltered_key[i] == "{") {
            level++;
​
        }
        else if (unfiltered_key[i] == "}") {
            level--;
        }
        temp.first = level;
        temp.second = unfiltered_key[i];
        level_key.push_back(temp);
    }

这边利用到‘{’,'}'的特点,去进行代码关键词所处的嵌套等级的判断。


switch组的判断

int SwitchCounter(int begin) {
    //统计每组switch中的case数量
    int case_num = 0;//当前switch结构中case数量
    int i = 0;//当前搜索序号
    for (i = begin; i < level_key.size(); i++) {
        if (level_key[i].second == "switch") {
            int j;
            for (j = i + 1; j < level_key.size() && level_key[j].first > level_key[i].first; j++) {
                if (level_key[j].second == "case") case_num++;
                else if (level_key[j].second == "switch") {
                    j = SwitchCounter(j);
                }
            }
            case_counter.push_back(case_num);
            return j;
        }
    }
    return i;
}

实际上switch的个数在进行关键词读入的时候已经统计完成了,所以这个函数是为了统计switch内部的case数量


if组的判断

int IfCounter(int begin) {
	//扫描switch情况
	int i = 0;//当前搜索序号
	for (i = begin; i < level_key.size(); i++) {
		if (level_key[i].second == "if") {
			int j;
			int flag = 0;//0表示if结构,1表示else结构,2表示if,else if结构,3表示if,else if,else结构
			for (j = i + 1; j < level_key.size() && level_key[j].first >= level_key[i].first; j++) {
				if (level_key[j].second == "else" && level_key[j + 1].second == "if") {
					if (flag == 0) {
						flag = 2;
					}
					j++;
				}
				else if (level_key[j].second == "else") {
					if (flag == 0) {
						if_else_counter++;
					}
					else if (flag == 2) {
						if_elseif_else_counter++;
					}
					break;
				}
				else if (level_key[j].second == "if") {
					j = IfCounter(j);
				}
				else if ((level_key[i].first >= level_key[j].first) && level_key[j].second == "}" && (level_key[j + 1].second != "else" && level_key[j + 1].second != "if")) {
					i = j+1;
					return j;
				}
			}
			return j;
		}
	}
	return i;
}

分析关键词组中的if情况并进行判断,最低等级是if,然后通过不断升级的方式判断其他的情况。

测试

单元测试

性能测试

 

困难及解决办法

文件读取

一开始遇到的困难就是文件读取,对于ifstream的了解还是不够深,于是我在谷歌上查了一下具体的用法是什么,然后找到了对应的用法。

关键词切分

这个的主要问题是在于关键词种类比较多,还有符号也是相当的多。后来想到可以用map的方式,直接对查找到的关键词通过对组的方式进行计数。这里使用到的切分方法是手写的,如果使用到正则表达式应该会提高不少效率,考虑下一个版本将切分单独出一个函数,并且用正则表达式进行匹配

代码规范

之前写代码的时候都蛮随意的,特别是变量和函数的命名,这次要求遵照代码规范一下子有一些不习惯了突然。但是代码规范在代码逐渐边长的时候优势就体现出来了。遵照代码规范进行开发能够大大增加代码的可读性,过两天再看代码的时候也不至于第一天只有我懂,第二天我都看不懂了。

初识git

在这之前,自己写代码从来没有做过版本控制,所以蛮凌乱的。对于github的使用也不是特别了解。为此我专门去b站学习了一下git的使用方法,再回过头一看,其实相当多的IDE内部都提供了git的相关快捷操作,如果用好这个的话其实是可以提高开发效率的。相对于原生git来说,GUI界面毕竟更好操作一些。

时间估算

发现自己对于项目时间的估计还是天真了,我觉得这很大一部分还是因为项目做得太少,对每个问题没有一个相对明确的直觉。这也导致了coding的时间大大超过了原有的计划,在调试自己的bug上也花了不少时间。

总结

第一次这么完整的做一个项目,感慨还是蛮多的。毕竟是个人做项目,所以无论遇到什么困难都要自己想办法解决。这个项目主要让我适应了一下完整的项目流程,以及知道了如何使用各项工具进行版本控制,debug以及性能分析与改进,这其实才是这一次作业最大的收获。我们以后遇到的问题会五花八门,甚至使用的编程语言以及知识也大不相同,如何迈出自己项目的第一步,在我看来这个才是最难的。

 

...全文
254 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

189

社区成员

发帖
与我相关
我的任务
社区描述
福州大学软件工程教学,推行邹欣老师“构建之法”。
软件工程 高校
社区管理员
  • Dawnfox
  • REP1USONE
  • 纪华裕
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧