这个作业属于哪个课程 | 构建之法-2021秋-福州大学软件工程 |
---|---|
这个作业要求在哪里 | 2021秋软工实践第一次个人编程作业 |
这个作业的目标 | 实现一个程序功能,它可以对读入的C或C++代码文件进行不同等级的关键字提取 |
学号 | 031902641 |
PSP Stages | 预估耗时(小时) | 完成时间(小时) |
---|---|---|
计划 | 0.5 | 0.5 |
时间预估 | 0.1 | 0.2 |
开发 | - | - |
需求分析 | 1.5 | 2 |
生成设计文档 | - | - |
设计复审 | 0.4 | 0.3 |
代码规范 | 1 | 1 |
具体设计 | 2 | 3 |
具体编码 | 15 | 20 |
代码复审 | 0.2 | 0.25 |
测试 | 1 | 2 |
报告 | 2 | 2.5 |
测试报告 | 0.5 | 0.5 |
计算工作量 | 0.1 | 0.1 |
总结与提高 | 0.5 | 1 |
总计 | 24.8 | 33.35 |
实现一个程序功能,它可以对读入的C或C++代码文件进行不同等级的关键字提取。
基础要求:输出关键字统计信息
进阶要求:输出有几组switch case结构,同时输出每组对应的case个数
拔高要求:输出有几组if else结构
终极要求:输出有几组if,else if,else结构
在完成更高要求的情况前,需完成前置的要求。
1. 选择用 Java 编写本次作业
2. 从键盘读入文件路径和等级要求,要使用 Scanner 类进行接收
3. 读入 C 或 C++ 代码,那么就要使用文件流进行读操作,并且把读入的内容存储到字符串中
4. 要统计关键词,要进行关键词匹配和统计,可以使用字符串的方法 equals() 进行比较
5. 统计 switch case 结构数,以及对每组对应的 case 个数进行统计
6. 统计 if-else 和 if-else if-else 结构,产生的嵌套匹配可能需要用到数据结构的知识
7. 根据输入的等级要求调用不同的方法,最终结果输出在控制台
基本步骤理清之后,整个作业的结构已经差不多搭建好
虽然结构好像很清晰,但是心中还是很多对细节的疑惑,尤其是对步骤 5 和 6,总觉得很复杂。
疑惑一:c 或者 cpp 文件的内容直接全部拼接成一个字符串会不会超过 String 能存的最大长度?分多个字符串存?String 类型数组?
解答:选用 StringBuilder 的 append() 方法,具体可参考
String、StringBuffer与StringBuilder之间区别
疑惑二:如何计算每个 switch 块里的 case 个数? 用 default 来判断当前代码块结束?那要是没 default 怎么办?
解答:咨询带佬之后决定使用括号匹配。每个 switch 后的作用域都是由一个 {}
围起来的,把 switch 后的 {
入栈,遇到 }
出栈,栈空之时代表当前 switch 作用域结束。
疑惑三:如何区别 if-else 和 if-else if-else?
解答:使用栈进行匹配和区分,将 if 和 else if 都压栈,遇到 else 出栈,如果栈顶是 if ,则为 if-else 结构;如果栈顶是 else if,则为if -else if-else 结构。
疑惑四:单元测试和性能测试是什么,使用什么工具?
解答:单元测试使用 ,具体参考Java单元测试初体验
性能测试工具用的是JProfilerIntelliJ IDEA集成JProfiler,入门教程
完整代码在keyWords
/* * 对文本进行预处理 * */ while(line != null) { // 去掉字符串 char quotationMark = '"'; if(line.indexOf(quotationMark) != -1) { CharBetween charBetween = new CharBetween(quotationMark, line); line = charBetween.getBetweenString(); } // 去掉单行注释 String noteInline = "//"; if(line.contains(noteInline)) { int tmp = line.indexOf(noteInline); if(tmp == 0) { line = bufferedReader.readLine(); // 读取下一行 continue; } else line = line.substring(0, tmp); } // 拼接字符串 fileContent.append(line); line = bufferedReader.readLine(); // 读取下一行 } // 去掉多行注释 fileContent = new TrimNote().trimNote(fileContent);
去除多行注释的方法
public StringBuilder trimNote (StringBuilder string) { String noteBlockStart = "/*"; String noteBlockEnd = "*/"; while(string.indexOf("/*") != -1) { int startIndex = string.indexOf(noteBlockStart); int endIndex = string.indexOf(noteBlockEnd, startIndex + 1); if(startIndex < 0 || endIndex < 0) break; string.replace(startIndex, endIndex + 2, ""); } return string; }
2.1 流程图
2.2 代码
for (String keyWord : keyWords) { String tmp = fileContent.toString(); count = 0; caseCountTmp = 0; int flag = 0; while (tmp.contains(keyWord)) { // 判断关键字是否包含在变量中 如果有这样的变量则删去 char pre = tmp.charAt(tmp.indexOf(keyWord) - 1); char after = tmp.charAt(tmp.indexOf(keyWord) + keyWord.length()); if (judgeItemInString(pre) || judgeItemInString(after)) {} else { count++; if (keyWord.equals("case") && flag == 0) { String tmp2 = tmp; for(int i = tmp2.indexOf("switch"); i < tmp2.length() && tmp2.contains("switch"); i++) { if(tmp2.charAt(i) == '{') { bracket.push("{"); } if(tmp2.charAt(i) == '}') { bracket.pop(); if(bracket.size() == 0) { caseCount.add(caseCountTmp); caseCountTmp = 0; tmp2 = tmp2.substring(i + 1); i = tmp2.indexOf("switch"); } } if(tmp2.contains("switch")) { if(tmp2.charAt(i) == 'c' && tmp2.charAt(i+1) == 'a' && tmp2.charAt(i+2) == 's' && tmp2.charAt(i+3) == 'e') { caseCountTmp++; } } } flag = 1; } } tmp = tmp.substring(tmp.indexOf(keyWord) + keyWord.length()); } map.put(keyWord, count); }
3.1 思路
将 if 和 else if 都压栈,遇到 else 出栈,此时进行计数。如果栈顶是 if ,则为 if-else 结构;如果栈顶是 else if,则为if -else if-else 结构。
3.2 代码
public void statisticsKeyWord(int level) { int ifElseCount = 0; int ifElseIfElseCount = 0; for(int i = 0; i < this.keyWordTmp.size(); i++) { if(this.keyWordTmp.get(i).equals("1")) this.keyWord.push("1"); if(this.keyWordTmp.get(i).equals("2")) { this.keyWord.push("2"); } if(this.keyWordTmp.get(i).equals("3")) { if(this.keyWord.peek().equals("2")) { while(this.keyWord.peek().equals("2")) this.keyWord.pop(); ifElseIfElseCount++; } else ifElseCount++; this.keyWord.pop(); } } if(level == 3) { System.out.print("if-else num: " + ifElseCount); } if(level == 4) { System.out.println("if-else num: " + ifElseCount); System.out.println("if-elseif-else num: " + ifElseIfElseCount); } }
看 commit 的时间图基本可以看出迭代的过程
搭好基本的功能模块,实现输入 -> 实现文本预处理 -> 完成 level1 -> 完成 level2 -> 完成 level3 -> 完成 level4 -> 复盘、修复 bug
5.1 单元测试
5.2 性能测试
本次作业感觉对我这个初试 Java 的人来说还是有一定难度。但最难的感觉还是解题思路,到博文写完的这一刻 if-else 和 if-else if-else 匹配仍存在bug,如果有 if 没有 else 进行匹配的话答案就是错误的。尤其是性能测试和单元测试,看了一些博文还是挺懵的,仅仅是跟着示例当了一回机器人,更不要说性能优化了。希望在接下来的作业中能继续锻炼到我使用 Java 和数据结构知识解决实际问题的能力,据说 switch 嵌套很可怕,接下来除了解决上面的 bug 以外还会思考 switch 嵌套的问题,继续维护这份代码。