题目要求
基础要求:输出关键字统计信息
进阶要求:输出有几组switch case结构,同时输出每组对应的case个数
拔高要求:输出有几组if else结构
终极要求:输出有几组if,else if,else结构
任务分解
建立Git本地仓库
建立GitHub远程仓库并与本地仓库链接
制定代码规范
寻找解题思路
编写代码并进行测试
优化代码并上传本地仓库
编写博客
目录
一、PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 60 |
Estimate | 估计这个任务需要多少时间 | 780 | 1080 |
Development | 开发 | - | - |
Analysis | 需求分析 (包括学习新技术) | 120 | 150 |
Design Spec | 生成设计文档 | - | - |
Design Review | 设计复审 (和同事审核设计文档) | - | - |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 240 | 300 |
Code Review | 代码复审 | 120 | 240 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 150 |
Reporting | 报告 | 30 | 30 |
Test Report | 测试报告 | 15 | 15 |
Size Measurement | 计算工作量 | 15 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 780 | 1080 |
二、解题思路
1.基础要求
- 刚拿到题目的时候,其实最头痛的是用命令行读入文件,百度完发现这部分其实并不难。
- 然后是字符串分割,发现C++难以实现,最后无奈选择按行读入文件
- 对于关键字的匹配,我选择的是逐字符匹配原则,选择了一个比较暴力的算法。
- 后续的完善过程中,完成了注释和引号中的关键字排除
2.进阶要求
- switch/case的统计相比之下就要简单很多了,我的做法大概是设置一个标志和一个计数数组,遇到switch标志加1,并将标志作为计数器下标,遇到case则计数。
3.拔高要求、终极要求
- 虽然是两个等级,但是必须放在一起解决。我的做法就是重新制定一个关键词数组,只包含if else,同时将elseif作为一个新的关键词,重新按基础要求的关键词匹配原则筛出关键词。
- 具体分辨拔高和终极要求的方法是申请一个栈,遇到if和elseif则入栈,遇到else时,若栈顶为if,则pop掉一个栈顶元素,若为elseif则pop掉两个栈顶元素。同时对应计数器加1。
三、流程图
1.代码组织流程图
2.关键词统计
3.switch/case统计
4.if-else / if-elseif-else统计
四、主要代码
1.字符串比对函数
int bruteforce(string t, string p) { //按字符比对函数,参数分别为待比对字符串和模板字符串
int lenT = t.size();
int lenP = p.size();
int i, j;
for (i = 0; i <= lenT - lenP; ++i) {//循环遍历每个字符,
for (j = 0; j < lenP; ++j) { //以模板字符串长度遍历
if (t[i + j] != p[j]) { //存在不匹配字符,跳出循环
break;
}
}
if (t[i + j] == '/' || t[i + j] == '"')
return 0;
if (j == lenP && ((t[i - 1] < 'a' || t[i - 1]>'z') || (t[i + j + 1] < 'a' && t[i + j + 1]>'z'))) //排除注释和引号内容
return 1;
}
return 0;
}
2.关键字统计函数
void readbyline(char** argv) //读入命令行参数
{
char buffer[256];
fstream fFile(argv[1], ios::in | ios::out); //读取文件
while (!fFile.eof())
{
fFile.getline(buffer, 256, '\n'); //按行读取字符串
int lenth = strlen(buffer);
for (int i = 0;i < 31;i++) {
if (bruteforce(buffer, key[i])) { //调用比对函数
match[cnt1] = key[i];
cnt1++;
}
}
}
fFile.close();
}
3.switch/case统计函数
void level_2(string str[]) {
int temp1 = 0, temp2[100] = { 0 };
int tmp = 0;
for (int i = 0;i < cnt1;i++) {
if (str[i] == "switch"){ //检测switch
temp1++; //switch数目
tmp++; //标志加1
}
else if (str[i] == "case")
temp2[tmp]++; //统计case
}
}
4.统计if-else/if-elseif-else函数
void level_3and4(char** argv) {
level_2(match);
int temp1 = 0, temp2 = 0;
for (int i = 0;i < cnt2;i++) {
if (if_match[i] == "if")
stk.push(if_match[i]); //为if则push进栈
else if (if_match[i] == "else if" && stk.top() != "else if") //栈顶为if时将elseif push进栈
stk.push(if_match[i]);
if (if_match[i] == "else" && stk.top() == "if") {
stk.pop(); //检测到else时栈顶为if则pop一次
if_cnt++;
}
else if (if_match[i] == "else" && stk.top() == "else if") {
stk.pop();
stk.pop(); //栈顶为elseif时pop两次
elseif_cnt++;
}
}
}
五、单元测试和性能分析
六、本次作业总结
- 这次作业对我来说完全是一个新的体验。包括git和GitHub的使用、对文件的读入以及IDE的一些使用等等,是我之前几乎从来没有接触过的东西。
- 编写代码过程中遇到的最大的问题就是如何除去注释和引号中的关键字。由于我的代码采用的是逐字符比较,所以不管是正则表达式还是字符串分割,都没有效果。最后还是偷了一点懒,读到“ / ”和“ “ ”时,直接忽略。。虽然有点投机取巧,但还是有效果的。
- 相比代码,各种工具还有性能测试、单元测试等等全新的名词才是真正花费时间的东西。一直到打完这篇博客之前,我都没有搞明白单元测试的原理。这是一个很可惜的地方,但由于ddl近在眼前,也只能不了了之。以后要是有空的话(
一般没空),会继续研究的。 - 总的来说,这是一次非常深刻的编程体验。这几天投入的时间很多,学到的东西也很多,同时也深刻意识到了自己的不足。不管是对于代码的规范性还是可读性,我感觉做的都还不够。包括IDE的使用等等,由于之前一直使用的是devc++,导致这次使用VS出现了很大的阻碍,在网络上学了好久,也没有完全搞明白VS的使用。总而言之,这次的作业真的让我收获良多