Github项目地址:https://github.com/czmDeRepository/SoftwareWork/tree/master/work
一、题目描述
- 实现一个简单而完整的软件工具(源程序特征统计程序)。
- 进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。
- 进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。
二、WC 项目要求
- wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
- 实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
- 具体功能要求:程序处理用户需求的模式为:wc.exe [parameter] [file_name]
三、核心代码
- 获取文件字符缓存流
private static BufferedReader GetFileInmputStream(String fileName){ BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName))); } catch (FileNotFoundException e) { System.out.println("系统找不到指定路径文件"); } return bufferedReader; }
- 返回文件字符数
public static int CharCount(String fileName){ BufferedReader in = null; int count = 0; in = GetFileInmputStream(fileName); if (in == null){ return ERROR_NUM; } int result = ERROR_NUM; try { while((result = in.read()) != -1){ if(result != '\r' && result!='\n'){ count++; } } } catch (IOException e) { e.printStackTrace(); } finally { if (in != null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } return count; }
- 返回文件行数
public static int LineCount(String fileName){ BufferedReader read = null; int count = 0; read = GetFileInmputStream(fileName); if (read == null){ return ERROR_NUM; } try { while (read.readLine() != null){ count++; } // 速度较前者慢 // Iteratoriterator = read.lines().iterator(); // while (iterator.hasNext()){ // iterator.next(); // count++; // } } catch (IOException e) { e.printStackTrace(); }finally { if(read != null){ try { read.close(); } catch (IOException e) { e.printStackTrace(); } } } return count; }
- 返回文件单词数
public static int WordCount(String fileName){ BufferedReader in = null; int count = 0; in = GetFileInmputStream(fileName); if (in == null){ return ERROR_NUM; } try { String str = null; while((str = in.readLine()) != null){ //\\s+表示 空格,回车,换行等空白符 String[] split = str.split("\\s+"); count += split.length; } } catch (IOException e) { e.printStackTrace(); } finally { if (in != null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } return count; }
- 显示(代码行 / 空行 / 注释行)
public static void Complex(String filename){ int codeLine = 0; int annotationLine = 0; int blankLine = 0; // 判断是否处于多行注解内 boolean flag = false; BufferedReader bufferedReader = GetFileInmputStream(filename); if (bufferedReader == null){ return; } String strLine = null; String newLine = null; try { while ((strLine = bufferedReader.readLine())!= null){ if (flag) { if (strLine.endsWith("*/")){ flag = false; } annotationLine++; }else { // 去除空格 newLine = strLine.replaceAll("\\s*", ""); if("".equals(newLine)){ //空行 blankLine++; }else if (newLine.startsWith("/*")){ //注释行 if (!newLine.endsWith("*/")){ //去除/*单行注释*/情况 flag = true; } annotationLine++; }else if (newLine.startsWith("//") || newLine.startsWith("}//")){ //注释行 annotationLine++; }else { //代码行 codeLine++; } } } System.out.println("代码行:"+codeLine); System.out.println("空行:"+blankLine); System.out.println("注释行:"+annotationLine); } catch (IOException e) { e.printStackTrace(); }finally { if (bufferedReader != null){ try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
- 递归处理目录
public static void FolderCount(String operation, String folderName, String fileName){ File file = new File(folderName); if(file.isDirectory()){ int index = fileName.lastIndexOf('.'); if(index >= 0){ String extension = fileName.substring(index); String frontName = fileName.substring(0, index); File[] fileList = file.listFiles(); if (fileList.length > 0){ // String regex = ".*"+name+"\\.+"+extension+"$"; // for (String f:fileList){ // System.out.println(Pattern.matches(regex, f)); // System.out.println(f); // } // 递归遍历 if (!ReadFold(operation, file.getName(), fileList, frontName, extension)){ System.out.println("该目录下无对应文件"); } }else{ System.out.println("该目录为空"); } } }else{ System.out.println("参数不是文件夹或路径错误"); } } /*** * 递归子文件 * @param operation * @param fileList * @param frontName * @param extension */ public static boolean ReadFold(String operation, String parentName, File[] fileList, String frontName, String extension){ boolean flag = false; boolean childFlag = false; String fName = null; for (File f:fileList){ // 子文件是文件 if(f.isFile()){ fName = f.getName(); if (fName.indexOf(frontName) >=0 && fName.endsWith(extension)) { switch (operation) { case "-c": System.out.println("【"+parentName+"】下的【"+fName+"】文件的字符数:" + CharCount(f.getPath())); break; case "-w": System.out.println("【"+parentName+"】下的【"+fName+"】文件的单词数:" + WordCount(f.getPath())); break; case "-l": System.out.println("【"+parentName+"】下的【"+fName+"】文件的行数:" + LineCount(f.getPath())); break; case "-a": System.out.println("【"+parentName+"】下的【"+fName+"】文件的信息:"); Complex(f.getPath()); break; default: System.out.println("第二参数错误(-c,-w,-l)"); return false; } flag = true; } }else{ //子文件是目录 childFlag = ReadFold(operation, f.getName(), f.listFiles(), frontName, extension) || childFlag;
}
}
return (flag || childFlag);
}
- 主函数调用
/** * 正确传参 */ private static int FLAG_TWO = 2; /** * 出错误返回值 */ private static int ERROR_NUM = -1; public static void main(String[] args) { if(args.length >= FLAG_TWO){ switch (args[0]){ // 字符数 case "-c": long startC = System.currentTimeMillis(); System.out.println("字符数:"+CharCount(args[1])); System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startC); break; // 单词数 case "-w": long startW = System.currentTimeMillis(); System.out.println("单词数:"+WordCount(args[1])); System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startW); break; // 行数 case "-l": long startL = System.currentTimeMillis(); System.out.println("行数:"+LineCount(args[1])); System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startL); break; // 递归处理目录下符合条件的文件。 case "-s": if(args.length == 4){ long startS = System.currentTimeMillis(); FolderCount(args[1], args[2], args[3]); System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startS); }else{ System.out.println("正确格式为:wc.exe -s 【操作】 文件夹路径 文件名"); } break; // 返回更复杂的数据(代码行 / 空行 / 注释行)。 case "-a": long startA = System.currentTimeMillis(); Complex(args[1]); System.out.printf("耗时%d(毫秒)\n", System.currentTimeMillis()-startA); break; default: System.out.println("请输入正确参数【操作】+文件路径"); } }else { System.out.println("请输入正确参数【操作】+文件路径"); } }
四、项目测试
- 用exe4j将jar包转exe程序再测试
五、PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
10 |
15 |
· Estimate |
· 估计这个任务需要多少时间 |
200 |
300 |
Development |
开发 |
120 |
200 |
· Analysis |
· 需求分析 (包括学习新技术) |
15 |
10 |
· Design Spec |
· 生成设计文档 |
10 |
10 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
10 |
10 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
5 |
5 |
· Design |
· 具体设计 |
30 |
25 |
· Coding |
· 具体编码 |
100 |
200 |
· Code Review |
· 代码复审 |
10 |
15 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
10 |
30 |
Reporting |
报告 |
15 |
20 |
· Test Report |
· 测试报告 |
10 |
30 |
· Size Measurement |
· 计算工作量 |
15 |
20 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
15 |
30 |
合计 |
|
575 |
920
|
六、总结
实际时间总比预期长,敲代码容易改代码难,思路要清晰,读取文件用缓冲流效果更好,将jar包转exe再执行程序感觉比直接运行慢,” || “判断只要前者为真后者就不再执行!!!学会了简单使用git管理项目。