软工个人项目WC(Java实现)

WC个人项目(JAVA实现)

一、Github地址:https://github.com/GordonKowk/WC_Item

 


二、PSP表格

 

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning 计划 30 36
· Estimate · 估计这个任务需要多少时间 30 30
Development 开发 960 784
· Analysis · 需求分析  50 50
· Design Spec · 生成设计文档 30 43
· Design Review · 设计复审  20 20
· Coding Standard · 代码规范 20 20
· Design · 具体设计 80 60
· Coding · 具体编码 750 681
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 180 180
Reporting 报告 60 85
· Test Report · 测试报告 60 60
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 40 40
合计   2360 2159

 


 

三、遇到的问题&解题思路:

1)一开始着手这个项目的时候挺苦恼的,因为我第一次用cmd去运行一个java文件(妈呀,我才发现调控台原来是命令行),但是学习过后也是比较简单的。项目做出了多个java文件,就在命令行里编译多个,最后选出主类去运行。因为我用的是MyEclipse,写出的java自带包,必须去掉包后才能够让cmd运行时不报错。

2)不论是什么功能,先写入一个switch去判断每一个Processro,从简单的入手,基础功能-c,-w,-l,-a的功能都是我们课堂接触过的,如何把文件内容送入缓冲区也是学过的。能说不同的,是-a统计里对注释行的判断,“\\”可以通过判断一行文字内是否存在得知,“/*……*/”,则可以先判断注释首部,让判断处于注释状态,再去寻找末尾。在功能判断的代码中因为单独判断一个元素(第一个元素arr[0]通常是“-c”“-w”之类的,这种不是路径的字符串无法给予file)会报错,我便把“-x”图形界面和“退出”两个判断放出switch。

3)递归调用。统计当前文件夹下符合的要求的文件,包括子文件。对于当前目录,由于arr[length-1]是最后一个元素,也就是你本来想要统计的文件路径。通过String类的substring(),LastIndexOf()方法分开父级文件夹名字和文件后缀名,用file.isFile()和file.isDirectory()等方法去判统计当前目录下的文件夹数量和名字、相同后缀名的文件数量和名字,这些数据被放在不同的字符串数组里面。对于子文件,我创建一个递归方法sonProcessor(),接受的数据时之前统计好的文件夹名字和统计文件的后缀名。相同的,递归方法对文件夹的数组里每一个文件夹路劲再一次进行统计,如果有,继续调用自己(这样做会让输出结果从最里面的文件夹开始统计),直到没有文件。最后是输出数据问题,我之前只写了一个文本输入流,却给了四个功能方法去调用,导致的结果是统计出来的数据会一直累计,一个3552字数的文件,在第二次统计达到8000多,最后我是分开了四个不同的文本输送流才解决了问题(我不知道是否有更好的方法,因为这么多文本输送肯定是不现实的)。

4)通配符的问题。我一开始会比较考虑要不要去写“正则表达式”(正则表达式其实比switch更具有判断性),但是做好了递归功能后我发现可以用String类的substring(),LastIndexOf()方法,对于通用符“*”,截取文件路径最后一次出现“\”和“.”中间的地方,就是“*”通用符,因此在switch前可以加上一次判断,如果是“*”出现了,直接调用递归。

5)最后是图形界面,个人感觉是比较简单的,我设计了带有六个按钮和带有一个文本域的界面,分别对应-c,-w,-l,-a,综合和退出的功能,我先定义一个整数Type,功能按钮都给予能调用FileChooser的监听器,并且每个按钮都会改变Type的值,输出对应的统计内容。

 


 

四、设计与实现

主要分类只有三个,主类Wc,功能类Function_Directory以及界面类GUI_Frame。

主要调用流程:

 

 

五、测试运行

测试文件包括:三个java文件,一个txt文件,两个不相关文件夹、嵌套目录(文件内带有三个java文件,一个子文件夹又带有三个java文件)

Wc.java在CMD里面的运行界面:

 

 

基础功能与扩展功能-a:

 

 

 

递归调用-s与通用符“*”:

 

 

 图形界面:

 

 

 

Wc.java

复制代码
static boolean TRUE_OR_NOT = true;
 
 public static void main(String[] args){
  System.out.println("*-------------------WC统计-------------------*");
  System.out.println("基础功能说明:");
  System.out.println("  统计字符数:-c [您要查找的文件路径]");
  System.out.println("  统计词数:  -w [您要查找的文件路径]");
  System.out.println("  统计行数:  -l [您要查找的文件路径]");
  System.out.println("");
  System.out.println("拓展功能说明:");
  System.out.println("  统计空行、代码行、注释行:    -a [您要查找的文件路径]");
  System.out.println("  递归处理:   -s [您要查找的文件路径]");
  System.out.println("");
  System.out.println("高级功能说明:");
  System.out.println("  图形化界面:-x");
  System.out.println("");
  System.out.println("若想退出程序请输入:退出");
  System.out.println("*--------------------------------------------*");
  System.out.println("请输入指令:"); 
  while(TRUE_OR_NOT){
   Scanner command = new Scanner(System.in);
   String[] arr = command.nextLine().split("\\s");
   int len = arr.length;
   Function_Directory FD = new Function_Directory();
   FD.commandProcessor(arr,len,0,arr[arr.length-1]); //把数据都交给FD对象里面
  }
 }
复制代码

GUI_Frame.java

复制代码
private static final long serialVersionUID = 1L;
 
 Toolkit          kit        = Toolkit.getDefaultToolkit();
 Dimension         screenSize   = kit.getScreenSize();
 static  JTextArea   textArea     = new JTextArea();
 private JPanel      chooseBar    = new JPanel();
 private JButton     count_c      = new JButton("字符数");
 private JButton     count_w     = new JButton("词数");
 private JButton     count_l      = new JButton("行数");
 private JButton     count_a     = new JButton("拓展功能");
 private JButton     count_all    = new JButton("总结");
 private JButton     count_close  = new JButton("关闭");
 private Font     buttonFont     = new Font("宋体",Font.BOLD,25);
 int       charNum        = 0;
 int       wordNum         = 0;
 int       lineNum        = 0;
 int       blankLineNum      = 0;
 int       codeLineNum       = 0;
 int       annotationLineNum  = 0;
 int       Type    = 0;
 ArrayList<Integer>    resultList    = null;
 
 
 GUI_Frame(){
  setTitle("WC统计");
  setSize(2*screenSize.width/4,3*screenSize.height/4);   
  setLocation(2*screenSize.width/4,screenSize.height/8);
  initEventListeners();     
  add(chooseBar,BorderLayout.NORTH);
  initChooseBar();
     initTextArea();
 }
 
 private void initChooseBar(){            //顶部选项栏设置
  chooseBar.add(count_c);
  chooseBar.add(count_w);
  chooseBar.add(count_l);
  chooseBar.add(count_a);
  chooseBar.add(count_all);
  chooseBar.add(count_close);
        count_c.setFont(buttonFont);
        count_w.setFont(buttonFont);
        count_l.setFont(buttonFont);
        count_a.setFont(buttonFont);
        count_all.setFont(buttonFont);
        count_close.setFont(buttonFont);
    }
 
 private void initTextArea(){
        textArea.setFont(new Font("宋体", Font.PLAIN, 30));
        textArea.setMargin(new Insets(3,10,3,10));
        textArea.setLineWrap(true);
        textArea.setDragEnabled(true);
        JScrollPane panel = new JScrollPane(textArea,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        getContentPane().add(panel, BorderLayout.CENTER);
    }
 
 private void initEventListeners(){
  count_c.addActionListener(  this::countC );
  count_w.addActionListener(  this::countW );
  count_l.addActionListener(  this::countL );
  count_a.addActionListener(  this::countA );
  count_all.addActionListener( this::countAll );
  count_close.addActionListener( this::countClose);
 }
 
 private void countC(ActionEvent event){
  Type = 1;
  CountStart();
 }
 private void countW(ActionEvent event){
  Type = 2;
  CountStart();
 }
 private void countL(ActionEvent event){
  Type = 3;
  CountStart();
 }
 private void countA(ActionEvent event){
  Type = 4;
  CountStart();
 }
 private void countAll(ActionEvent event){
  Type = 5;
  CountStart();
 }
 private void countClose(ActionEvent event){
  Type = 0;
  System.exit(0);
 }
 
 public void CountStart(){
  JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        chooser.showDialog(new JLabel(), "选择要统计的文件");
        File file = chooser.getSelectedFile();
        try{
   String encoding = "GBK";
   InputStreamReader readFile = new InputStreamReader(new FileInputStream(file),encoding);
   BufferedReader fileContent = new BufferedReader(readFile);
   switch(Type){
   case 1:
    charNum = Function_Directory.charsCounter(fileContent);
    textArea.append(file+":\n"+"字符数:" + charNum + "\n\n");
    break;
   case 2:
    wordNum = Function_Directory.wordsCounter(fileContent);
    textArea.append(file+":\n词数:" + wordNum+"\n\n");
    break;
   case 3:
    lineNum = Function_Directory.linesCounter(fileContent);
    textArea.append(file+":\n行数:" + lineNum+"\n\n");
    break;
   case 4:
    resultList = Function_Directory.Expand_Count(fileContent);
    blankLineNum = resultList.get(0);
    codeLineNum = resultList.get(1);
    annotationLineNum = resultList.get(2);
    textArea.append(file+":\n空行数:" + blankLineNum + "\n代码行数:"
      + codeLineNum + "\n注释行数" + annotationLineNum+"\n\n");
    break;
   case 5:
    charNum = Function_Directory.charsCounter(fileContent);
    wordNum = Function_Directory.wordsCounter(fileContent);
    lineNum = Function_Directory.linesCounter(fileContent);
    resultList = Function_Directory.Expand_Count(fileContent);
    blankLineNum = resultList.get(0);
    codeLineNum = resultList.get(1);
    annotationLineNum = resultList.get(2);
    textArea.append(file+":\n"+"字符数:" + charNum + "\n词数:"
      + wordNum + "\n行数:" + lineNum + "\n空行数:" + blankLineNum + "\n代码行数:"
      + codeLineNum + "\n注释行数" + annotationLineNum+"\n"); 
   }   
  }catch(IOException e){
    System.out.println("文件路径错误或者文件不支持喔~");
    e.printStackTrace();
  }
 }
 
 public static void main(String[] args) {
  GUI_Frame GF = new GUI_Frame();
  GF.setVisible(true);
 }
复制代码

Function_Directory.java

复制代码
public class Function_Directory {
 public void commandProcessor(String[] arr,int len,int start,String fileName){
  if(arr[0].equals("-x")){      //由于只输入一个元素会报错,“-x”和“退出”先提前判断
   GUI_Frame GF = new GUI_Frame();    //一个图形化界面
   GF.setVisible(true);
  } else if(arr[0].equals("退出")) { Wc.TRUE_OR_NOT = false;
  } else{
   try{
    for(int i = start;i < len-1||i == 0;i++){
     String fileUrl = arr[arr.length-1].substring(arr[arr.length-1].lastIndexOf("\\")+1,arr[arr.length-1].lastIndexOf("."));
     System.out.println(fileUrl);
     if(fileUrl.equals("*")) recursiveProcessor(arr); //判断通用符号“*”
     else{
      String encoding = "GBK";
      File file = new File(fileName);
      InputStreamReader readFile = new InputStreamReader(new FileInputStream(file),encoding);
      BufferedReader fileContent = new BufferedReader(readFile);   
     switch(arr[i]){
      case "-c":
       charsCounter(fileContent);
       break;
      case "-w":
       wordsCounter(fileContent);
       break;
      case "-l":
       linesCounter(fileContent);
       break;
      case "-a":
       Expand_Count(fileContent);
       break;
      case "-s":
       recursiveProcessor(arr);
       break;
      default:break;
     }
    }}
   }catch(Exception e){
    System.out.println(arr[0]+"不是功能指令喔~,看看是不是哪里输入错误了。");
    e.printStackTrace();
   }
  }
 }
 
 static int charsCounter(BufferedReader fileContent) throws IOException{
  String  lineCount  = null;
  int  charNum  = 0;
  while((lineCount  = fileContent.readLine()) != null){
   lineCount   = lineCount.trim();
   for(int i = 0;i < lineCount.length();i++){
    char ch  = lineCount.charAt(i);
    if(ch != '\n' && ch != '\t' && ch != ' ')
     charNum++;
   }
  }
  System.out.println("字符数:" + charNum);
  return charNum;
 }
 
 static int wordsCounter(BufferedReader fileContent) throws IOException{
  String  REGEX   = "[a-zA-Z]+\\b";  //判断词的正则表达式
  String  lineCount  = null;
  int  wordNum  = 0;
  Pattern pattern  = Pattern.compile(REGEX);
  while((lineCount = fileContent.readLine()) != null){
    lineCount  = lineCount.trim();
   Matcher matcher = pattern.matcher(lineCount);
   while(matcher.find()){
    wordNum++;
   }
  }
  System.out.println("词数:" + wordNum);
  return wordNum;
 }
 
 static int linesCounter(BufferedReader fileContent) throws IOException{
  int  lineNum  = 0;
  String  lineCount  = null;
  while((lineCount = fileContent.readLine()) != null){ lineNum++; }
  System.out.println("行数:" + lineNum);
  return  lineNum;
 }
 
 static ArrayList<Integer> Expand_Count(BufferedReader fileContent)throws IOException{
  String  lineCount  = null;
  ArrayList<Integer>  resultList   = new ArrayList<Integer>();
  boolean    isComment   = false;
  int  codeLineNum   = 0;
  int  blankLineNum   = 0;
  int  annotationLineNum  = 0;
  while((lineCount = fileContent.readLine()) != null){   //对注释行的判断
   if(lineCount.contains("/*")){
    annotationLineNum++;
    isComment = true;
   } else if(isComment){
    annotationLineNum++;
    if(lineCount.contains("*/")){ isComment = false; }
   } else if(lineCount.contains("//")){
    annotationLineNum++;
   } else if(lineCount.trim().length() > 1){
    codeLineNum++;
   } else{
    blankLineNum++;
   }
  } 
  System.out.println("空行数:" + blankLineNum);
  System.out.println("代码行数:" + codeLineNum);
  System.out.println("注释行数:" + annotationLineNum);
  resultList.add(blankLineNum);
  resultList.add(codeLineNum);
  resultList.add(annotationLineNum);
  return resultList;
 }
 
 public void recursiveProcessor(String[] arr) throws IOException{
  int   LA    = arr.length;
  int   n    = 0;
  String   fileUrl   = arr[LA-1].substring(0,arr[LA-1].lastIndexOf("\\")); //找到文件路径最先出现“\”的位置
  String   fileEnd   = arr[LA-1].substring(arr[LA-1].lastIndexOf("."));  //找到文件路径最先出现“.”的位置
        List<File>  fileList   = new ArrayList<File>();
        File   file    = new File(fileUrl);
        File[]   files    = file.listFiles();
        String[]    names   = file.list();
        String[] CompletNames  = null;
        for (File f : files) {
         if (f.isDirectory()){ n++;    }
        }
        CompletNames    = new String[n];
        n       = 0;
        if (files == null) {}
        for (File f : files) {
            if (f.isFile()&&f.getName().endsWith(fileEnd)) {  fileList.add(f); }
            if (f.isDirectory()){
             CompletNames[n] = arr[LA-1].substring(0,arr[LA-1].lastIndexOf("\\"))+"\\"+f.getName();
             System.out.println(CompletNames[n]);
             n++;
            }
        }
        for (File f1 : fileList) {
         System.out.println(fileUrl+"\\"+f1.getName());
   String encoding = "GBK";
   InputStreamReader rC = new InputStreamReader(new FileInputStream(f1),encoding);
   InputStreamReader rW = new InputStreamReader(new FileInputStream(f1),encoding);
   InputStreamReader rL = new InputStreamReader(new FileInputStream(f1),encoding);
   InputStreamReader rA = new InputStreamReader(new FileInputStream(f1),encoding);
    BufferedReader C = new BufferedReader(rC);
    BufferedReader W = new BufferedReader(rW);
    BufferedReader L = new BufferedReader(rL);
    BufferedReader A = new BufferedReader(rA);
     charsCounter(C);
     wordsCounter(W);
     linesCounter(L);
     Expand_Count(A);
   C.close();        //我试过只写一个文本输入流,统计结果会累积,数据错误
   W.close();        //由于写入太多输入流,希望想找到更好的办法
   L.close();
   A.close();       
        }
  System.out.println("*-----------------------------*");
  if (CompletNames!=null) sonProcessor(CompletNames,fileEnd); 
 }
 private void sonProcessor(String[] Names,String End) throws IOException { 
  String[]     N     = Names;    //寻找子文件,并把子文件里面符合要求的文件统计出来
  int    LengthA   = N.length;    //这里我运用了递归,但是发现是先递归到最里层才开始统计
  for(int i=0;i<Names.length;i++){
   int   n    = 0;
   String   fileUrl   = N[i];
   String   fileEnd   = End;
   List<File>  fileList   = new ArrayList<File>();
         File   file    = new File(fileUrl);
         File[]   files    = file.listFiles();
         String[]    names   = file.list();
         String[] CompletNames  = null;
         for (File f : files) {
          if (f.isDirectory()){ n++; }
         }
         CompletNames    = new String[n];
         n       =0; 
         if (files == null) {}
         for (File f : files) {
             if (f.isFile()&&f.getName().endsWith(fileEnd)) {  fileList.add(f); }
             if (f.isDirectory()){
              CompletNames[n]  = N[i]+"\\"+f.getName();
              System.out.println(CompletNames[n]);
              n++;
             }
         }
         if (CompletNames!=null)  sonProcessor(CompletNames,fileEnd);
         for (File f1 : fileList) {
    System.out.println(N[i]+"\\"+f1.getName());
    String encoding = "GBK";
    InputStreamReader rC = new InputStreamReader(new FileInputStream(f1),encoding);
    InputStreamReader rW = new InputStreamReader(new FileInputStream(f1),encoding);
    InputStreamReader rL = new InputStreamReader(new FileInputStream(f1),encoding);
    InputStreamReader rA = new InputStreamReader(new FileInputStream(f1),encoding);
     BufferedReader C = new BufferedReader(rC);
     BufferedReader W = new BufferedReader(rW);
     BufferedReader L = new BufferedReader(rL);
     BufferedReader A = new BufferedReader(rA);
      charsCounter(C);
      wordsCounter(W);
      linesCounter(L);
      Expand_Count(A);
    C.close();
    W.close();
    L.close();
    A.close();
    System.out.println("*-----------------------------*");
         }
  }
 }
复制代码

 六、项目总结:

   这次项目让我感受最深的是做这个项目的计划,先给自己一个规划,做出要想做的框架流程,想好自己应该干嘛,用多少时间,应该怎么去测试,以前的课设我是没有注意到这些的,这也让我看到做一个项目的严谨性。之前的java程序我都是通过设计图形化界面去运行的,没有像这次在命令行里面运行,学会了如何在命令行编译与运行java文件。递归调用的实现也是我印象最深刻的,文件夹和文件的路径由于循环让我自己都搞晕了,不断调试,不断找准切分位置,不断有报错,文件夹数组长度不能过长,路径不能错误,有时数据会卡住循环进行。递归的实现是我用时最多的部分。但是因为想去搞懂,从网上找资料,询问同学,学到不少,也能想出适合自己的方法。现在是个人项目,我期待在后面的项目能收获更多。

posted @ 2019-09-23 05:39  GordonKowk  阅读(324)  评论(1编辑  收藏  举报