github项目地址:https://github.com/CuiLam/SoftwareEngineering-wc
项目相关要求
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
-
具体功能
-
-c 返回文件字符数
-
-w 返回词的数目
-
-l 返回行数
-
-
扩展功能
-
-s 递归处理目录下符合条件的文件
-
-a 返回更复杂的数据(代码行 / 空行 / 注释行)
-
-
高级功能
-
-x 命令行输入-x参数,程序显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
-
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 30 | 20 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 180 | 90 |
· Design Spec | · 生成设计文档 | 180 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 15 |
· Design | · 具体设计 | 30 | 10 |
· Coding | · 具体编码 | 300 | 360 |
· Code Review | · 代码复审 | 30 | 15 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 300 | 180 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 10 |
合计 | 1210 | 860 |
解题思路
项目要求分三级,具体、扩展和高级。
具体功能及整个项目的核心基础功能,因此应该从具体功能入手。完成了单文件之后再去进行多文件和图形化界面选择文件将会简单很多。
通览项目要求,是对文件/文件夹进行统计,可以说是一个简单的文件/文件夹的工具类。
-c
文件字符数
在这里我将文件字符数归结为文件中可见的字符数量,即\n\r\t
等并未列入计算。
基于这样的定义和考虑,打算从IO流中读取文件每行并采用String.length()
方法计算文件字符数。
-w
词的数目
关于词的数目在这里定义为单个英文字母/连续的英文字母或纯数字组合或英文字母和数字的组合。
因此在项目中设计使用正则来进行统计。
-l
行数
在行数方面的设计并没有做多想,仅仅通过读取IO流行数统计文件行数
-a 代码行 / 空行 / 注释行
在这一个参数上的考虑是从容易统计的进行。
空行
全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”
判断空行分三步
-
使用
String.trim();
去除行中首尾空白 -
判断去除首尾空白之后长度
-
使用
String.length()
计算剩余长度若长度等于0则视为空行
如果等于1,则判断这个剩余这一个长度的字符串是不是非单词字符
若是,则该行为空行
否则,部位空行
如果长度大于1,则视为非空行
注释行
//
/** */``}\\
在项目中被定义为注释行
在这里计划使用正则表达式进行匹配统计
代码行
除开空行和注释行其余的被视为代码行
设计实现过程
项目设计有两个类,如下图所示
在main()
中使用循环解析参数,参数字符串以-
开头的视为参数,其余视为路径
根据路径再分文件和文件夹的处理
如果是文件夹则进行递归文件处理
代码说明
-
main()
参数的处理List
params = new ArrayList<>(); for (String s : args) { if (s.equals("-x")) { main.showGui(); } else if(s.startsWith("-")) { params.add(s); } else if (isFile(s)) { System.out.println(s); for (String param : params) main.operSingleFile(s, param); } else if (isDirectory(s)) { main.operFiles(s, params, null); } else { //s可能是通配符 String currentPath = main.getCurrentPath(); main.operFiles(currentPath, params, getRegex(s)); return; } } -
文件行数统计
while (bufferedReader.readLine() != null) line++;
-
单行统计单词个数
String s = string.trim(); //首先去除字符串收尾缩进 String regex = "[a-zA-Z0-9]+\\b"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(s); while (matcher.find()) { count++; }
-
统计字符数
while ((line = bufferedReader.readLine()) != null) count += line.length();
-
判断是否是注释行
String regex = "^\\W*$"; //如果包含这些表示可能是注释行 //包含并且注释符号前全为非字符的一定是注释行 if (line.contains("//")) { return line.split("//").length == 0 || Pattern.matches(regex, line.split("//")[0]); } else if (line.contains("/**")) { return Pattern.matches(regex, line.split("//*/*")[0]); } else if (line.contains("*/")) { return Pattern.matches(regex, line.split("/*/")[0]); } else if (line.contains("")) { return Pattern.matches(regex, line.split("//")[0]); } else if (line.contains("*")) { return Pattern.matches(regex, line.split("//*")[0]); }
测试运行
-
运行参数
-l -w -c /home/lam/gdut/SoftwareEngineering/DemoOne/src/com/lam/MainIndex.java
输出情况
/home/lam/gdut/SoftwareEngineering/DemoOne/src/com/lam/MainIndex.java 行数:155 单词个数:452 字符数:5031
-
运行参数
-l -w -c -a/home/lam/gdut/SoftwareEngineering/DemoOne/src/com/lam
输出
/home/lam/gdut/SoftwareEngineering/DemoOne/src/com/lam/util/FileUtil.java 行数:247 单词个数:582 字符数:7226 空行:39 代码行:153 注释行:55 /home/lam/gdut/SoftwareEngineering/DemoOne/src/com/lam/MainIndex.java 行数:155 单词个数:452 字符数:5031 空行:32 代码行:121 注释行:2
-
运行参数
-x
-
单元测试
代码覆盖率如下图所示
项目小结
这是我第一次在实践项目中使用软件工程的思想。对于一开始PSP表格的时间预计,有些拿捏不准。对于自己每项任务完成时间的估计没有考量。但是总归来说在项目开始还是有自己的项目阶段步骤的考虑。
整个项目实践下来还不够成熟和完善。
在一些需求的考虑上考量不足,单元测试之后仍有改动。
希望这一次的练习能为接下来的项目打下基础。