189
社区成员




这个作业属于哪个课程 | 构建之法-2021秋-福州大学软件工程 |
---|---|
这个作业要求在哪里 | 2021秋软工实践第一次个人编程作业 |
这个作业的目标 | 实现一个程序功能,它可以对读入的C或C++代码文件进行不同等级的关键字提取。 |
学号 | 031902124 |
代码规范参照Google开源项目风格指南(C++)
已上传至github仓库
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
Estimate | 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | ||
Analysis | 需求分析 (包括学习新技术) | 120 | 150 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 50 | 50 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 180 | 300 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 180 |
Reporting | 报告 | ||
Test Report | 测试报告 | 20 | 30 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 700 | 920 |
在饼图中我们可以看到,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的方式,直接对查找到的关键词通过对组的方式进行计数。这里使用到的切分方法是手写的,如果使用到正则表达式应该会提高不少效率,考虑下一个版本将切分单独出一个函数,并且用正则表达式进行匹配
之前写代码的时候都蛮随意的,特别是变量和函数的命名,这次要求遵照代码规范一下子有一些不习惯了突然。但是代码规范在代码逐渐边长的时候优势就体现出来了。遵照代码规范进行开发能够大大增加代码的可读性,过两天再看代码的时候也不至于第一天只有我懂,第二天我都看不懂了。
在这之前,自己写代码从来没有做过版本控制,所以蛮凌乱的。对于github的使用也不是特别了解。为此我专门去b站学习了一下git的使用方法,再回过头一看,其实相当多的IDE内部都提供了git的相关快捷操作,如果用好这个的话其实是可以提高开发效率的。相对于原生git来说,GUI界面毕竟更好操作一些。
发现自己对于项目时间的估计还是天真了,我觉得这很大一部分还是因为项目做得太少,对每个问题没有一个相对明确的直觉。这也导致了coding的时间大大超过了原有的计划,在调试自己的bug上也花了不少时间。
第一次这么完整的做一个项目,感慨还是蛮多的。毕竟是个人做项目,所以无论遇到什么困难都要自己想办法解决。这个项目主要让我适应了一下完整的项目流程,以及知道了如何使用各项工具进行版本控制,debug以及性能分析与改进,这其实才是这一次作业最大的收获。我们以后遇到的问题会五花八门,甚至使用的编程语言以及知识也大不相同,如何迈出自己项目的第一步,在我看来这个才是最难的。