软工作业1——wc.exe项目(Java实现)

 
 

项目要求

  wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
  实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
  •   基本要求
  • -c  统计文件字符数(实现)
  • -w 统计文件单词数(实现)
  • -l   统计文件行数   (实现)
  •       扩展要求
    • -s  递归处理目录下符合条件的文件     (实现)
    • -a  返回文件代码行、空行、注视行数  (实现)
  •       高级要求
    • -x  程序图形界面化  (未实现)
 

PSP表格预估

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 40 60
· Estimate · 估计这个任务需要多少时间 40 60
Development 开发 1200 1620
· Analysis · 需求分析 (包括学习新技术) 120 150
· Design Spec · 生成设计文档 40 60
· Design Review · 设计复审 (和同事审核设计文档) 40 55
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 35 45
· Design · 具体设计 130 160
· Coding · 具体编码 685 960
· Code Review · 代码复审 70 90
· Test · 测试(自我测试,修改代码,提交修改) 80 100
Reporting 报告 240 400
· Test Report · 测试报告 120 220
· Size Measurement · 计算工作量 60 80
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 100
合计   1480 2080
 
 
 

解题思路

        
  根据需求,应将总体分割为“实现功能的函数”和“负责运行的主函数”两个。
  设计过程中由于很多个环节都需要用到检验功能,检验文件、检验命令、检验文件夹等等,于是我将其检验功能单独取出来做成一个函数,方便其余各函数调用以及减少代码重复率。
  同时,由于递归函数功能跟其他功能相比之下有点特殊,所以我又将其独立成一个函数。
 

代码说明

 
  Count函数共有6个方法,其中一个为传入参数的构造方法
  charNumber、wordNumber、lineNumber   相对应着  “ -c ”  “ -w ”   “-l ”  三个基本功能
  additionlineNumber  则是实现了  “ -a ”  这个扩展功能
  runFunc   则是根据输入指令来选择相应功能的运行
package wc;

import com.mysql.jdbc.StringUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException; public class Count { int charNum ; int wordNum ; int lineNum ; int spacelineNum; int notelineNum; int codelineNum; String[] cms; String contentLine; File file; BufferedReader br; public Count(String[] commands , File file){ cms = commands.clone(); this.file = file; System.out.println("文件:"+file.getAbsolutePath()); } public void charNumber() throws IOException{ charNum = 0; br = new BufferedReader(new FileReader(file)); while(( contentLine = br.readLine())!=null){ String str = contentLine.replaceAll("\\s",""); charNum += str.length(); } System.out.println("字符数:"+charNum); } public void wordNumber() throws IOException{ wordNum = 0; br = new BufferedReader(new FileReader(file)); //用空格替换掉非单词的字符,并用StringUtils类将其切割 while(( contentLine = br.readLine())!=null){ String str = contentLine.replaceAll("\\W"," "); wordNum += StringUtils.split(str," ",true).size(); } System.out.println("单词数:"+wordNum); } public void lineNumber() throws IOException{ lineNum =0; br = new BufferedReader(new FileReader(file)); while(( contentLine = br.readLine())!=null){ lineNum++; } System.out.println("行数:"+lineNum); } //统计空白行、代码行和注释行的数目 public void additionlineNumber() throws IOException{ spacelineNum = 0; notelineNum = 0; codelineNum = 0; boolean flag = false; br = new BufferedReader(new FileReader(file)); while(( contentLine = br.readLine())!=null){ contentLine = contentLine.trim(); if(contentLine.matches("^\\s*$")){ spacelineNum++; }else if(contentLine.startsWith("/*")&&!contentLine.endsWith("*/")){ notelineNum++; flag = true; }else if (flag == true){ notelineNum++; if(contentLine.endsWith("*/")){ flag = false; } }else if(contentLine.startsWith("//")||contentLine.endsWith("//")){ notelineNum++; }else{ codelineNum++; } } System.out.println("空白行:"+spacelineNum); System.out.println("注释行:"+notelineNum); System.out.println("代码行:"+codelineNum); } public void runFunc() throws IOException{ for(String command : cms) { switch (command) { case "-c": charNumber(); break; case "-w": wordNumber(); break; case "-l": lineNumber(); break; case "-a": additionlineNumber(); break; default: System.out.println("输入功能有误"); break; } } } }
 
 
        CheckOut函数,关于检验文件、文件夹、代码文件和指令的功能皆包含在内
package wc;

import java.io.*;
import java.util.Arrays;

public class CheckOut {

    String[] cms;
    File file;
    String[] cmCol = {"-c","-w","-l","-s","-a"}; //指令数组,用于检验输入指令是否正确

    public CheckOut(String[] command,File file){
        cms = command.clone(); this.file = file; } public boolean checkFile() { if (file.isFile() && file.exists()) { return true; } else { System.out.println("该文件不存在!请检查路径是否输入错误!"); return false; } } public boolean checkCommand(){ for(String cm : cms){ if(!Arrays.asList(cmCol).contains(cm)){ System.out.println("输入命令有误,请重新输入!"); return false; } } return true; } public static boolean checkFile(File file){ if(file.isFile()&&file.exists()){ return true; }else{ return false; } } public static boolean checkFileDir(File file){ if(file.isDirectory()){ if(file.list().length>0){ return true; }else{ System.out.println("该目录为空目录!"); return false; } }else{ return false; } } public static boolean checkCodeFile(File file){ String str = file.getName(); if(str.endsWith(".java")||str.endsWith(".cpp")||str.endsWith(".c")){ return true; }else{ return false; } } }
 
 
        RecFile函数,对输入文件夹进行递归处理,查询目录下符合条件的所有代码文件
 
package wc;

import java.io.File;
import java.io.IOException;

public class RecFile {

     public static void RecF(String[] cms,File file) throws IOException{
        if(CheckOut.checkFile(file)&&CheckOut.checkCodeFile(file)){ Count count = new Count(cms,file); count.runFunc(); }else if(file.isDirectory()) { File[] files = file.listFiles(); for (File fs : files) { RecF(cms, fs); } } } }

      主函数Main,整个程序的执行函数

package wc;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;

public class Main { public static void main(String[] args) throws IOException{ System.out.println("\n\t 输入 “-c 文件完整路径” 表示查询文件的字符数 "); System.out.println("\n\t 输入 “-w 文件完整路径” 表示查询文件的词的数目"); System.out.println("\n\t 输入 “-l 文件完整路径” 表示查询文件的行数"); System.out.println("\n\t 输入 “-a 文件完整路径” 表示查询文件的空行、注释行、代码行数"); System.out.println("\n\t 输入 “-s 其他功能指令 文件完整路径” 表示以特定功能查询文件夹下所有代码文件"); System.out.println("\n\t 输入 “-e” 表示退出程序"); System.out.println("\n\t 以上除了退出功能外,其余皆可叠加指令查询,如-c -w -l 文件完整路径"); while(true){ System.out.println("\n请输入相应命令:"); Scanner sc = new Scanner(System.in); //按空格切割成字符串数组,并将其分为指令组和文件路径 String commands[] = sc.nextLine().toString().split("\\s"); //判断是否为退出指令 if(commands[0].equals("-e")){ break; } String cms[] = Arrays.copyOf(commands,commands.length-1); String filePath = commands[commands.length-1]; File file = new File(filePath); //递归查询 if(cms[0].equals("-s")) { //检验输入指令除了‘-s’外,还含有其他功能指令 if(cms.length>1){ String newcms[] = Arrays.copyOfRange(cms,1,cms.length); if(CheckOut.checkFileDir(file)) { RecFile.RecF(newcms, file); } }else{ System.out.println("输入指令有误,请重新输入!"); } } //普通查询 else{ CheckOut co = new CheckOut(cms,file); if(co.checkCommand()&&co.checkFile()) { if(CheckOut.checkCodeFile(file)) { Count count = new Count(cms, file); count.runFunc(); }else{ System.out.println("该文件不是代码文件,请重新输入!"); } }else{ continue; } } } } }
 

测试

  程序已打包成exe文件并上传至github

      
  
  
  
 

代码覆盖率

  

总结

   虽说图形界面没有完成,但还是勉强强将其他功能给实现了。在实现的过程中,由于之前的java知识遗忘掉蛮多的,所以在编程中都是边想边打码边百度的。

     其中也碰到不少坑,例如为了单词的正则表达式烦恼了许久,最后还是决定用\\W这种并不算严谨的方式匹配;还有exe4j的坑,由于我在Count类wordNumber方法里切割单词时,采用的是StringUtils的切割方法,所以要用到工具包,于是在将项目打包成jar时吃了不少亏,最后的解决方法是已导出的、将没有工具包的项目jar跟工具包jar解压到同一个文件,同时让项目jar的MANIFEST.MF文件覆盖掉后者的文件,最后再将其压缩成一个jar文件。

  这个项目的不足之处还有不少,例如检验功能并没有能完全独立出来,代码的耦合性有点高、不太易于维护和升级,倘若要改进的话,需先从这两处着手才行。

猜你喜欢

转载自www.cnblogs.com/ouroborous/p/9638309.html