个人项目作业

帅比萌擦擦* 提交于 2020-03-14 12:39:39

项目地址:https://github.com/LixinXie/WordCounter

正文

一、WC 项目要求

wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。

实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:

wc.exe [parameter] [file_name]

 

基本功能列表:

wc.exe -c file.c     //返回文件 file.c 的字符数

wc.exe -w file.c    //返回文件 file.c 的词的数目  

wc.exe -l file.c      //返回文件 file.c 的行数

 

扩展功能:
    -s   递归处理目录下符合条件的文件。
    -a   返回更复杂的数据(代码行 / 空行 / 注释行)。

空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。

代码行:本行包括多于一个字符的代码。

注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:

    } //注释
在这种情况下,这一行属于注释行。

[file_name]: 文件或目录名,可以处理一般通配符。


高级功能:

 -x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。

需求举例:
  wc.exe -s -a *.c


返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。

二、解题思路

  使用的编程语言:Java  

  使用到的工具: JDK 1.8、JUnit 5.4、 Intellij IDEA、Git

  需要学习的新知识:文件读取、图形化界面、字符串的相关操作、正则表达式等知识

  在开始拿到题目时我是很懵的,以前没有接触过这样的作业,也不知道如何下手。通过阅读资料,查看作业详情,和同学交流,逐渐的了解这个作业改怎么做,项目该怎么构建。一开始对Java的知识并不是很熟悉,通过做这个个人作业,我坚持着做中学,边做边学,一步一步的完成。

三、设计实现过程

  在设计初期,我估计基本功能需要三个类来实现,一个主类,一个GUI类,一个测试类;

  主类实现基本功能和扩展功能中的-a功能,主类中有基本功能的三个方法(-c、-w、-l)和特殊行(-a)的方法,这几个方法都是以有返回值的形式存在的,而不是直接在该方法中输出。后来为了实现递归遍历文件/文件夹,又在主类中加入了能返回文件绝对路径List<String>对象的方法,在这个方法里使用递归思想来获取文件名(绝对路径);GUI图形用户界面选择文件的类可以直接运行(需要调用主类里的方法)这里只实现了选择文件然后输出该文件的信息(这个信息通过弹窗对话来提示);测试类用来测试主类里面的各个方法(这里使用了JUnit的@Test注解让测试类中的每个方法能独立运行)。

  后来在构建过程中,发现特殊行的统计时三个值返回的问题,因此增加了一个类来封装特殊行的三个值,这样在统计特殊行和获取特殊行的数值时就能返回三个值了。

四、关键代码and设计说明

   1.实现统计字符数(包括中文字符,不含换行符)  

   注意:在UTF-8编码下和GBK编码下,同一个源文件的字符数统计可能不一样

   说明:在统计字符数时,使用了输入流InputStreamReader来读取每一行,BufferedReader缓存读到的每一行;再将缓存的每一行转化为字符串,统计各个字符串的长度之和就是所有字符的总数,但这里不包括每一行末尾的换行符。

 1 public static int charcount(String filename){
 2         //统计字符数,不包含换行
 3         int characters=0;
 4         String str = null;
 5         BufferedReader br = null;
 6         InputStreamReader reader = null;
 7         File file = new File(filename);
 8         try {
 9             reader = new InputStreamReader(new FileInputStream(file));
10             br = new BufferedReader(reader);
11             while((str = br.readLine())!=null){
12                 characters+=str.length();
13             }
14 
15         } catch (Exception e) {
16             e.printStackTrace();
17         }finally{
18             if(reader!=null){
19                 try {
20                     reader.close();
21                 } catch (IOException e) {
22                     e.printStackTrace();
23                 }
24             }
25             if(br!=null){
26                 try {
27                     br.close();
28                 } catch (IOException e) {
29                     e.printStackTrace();
30                 }
31             }
32         }
33         return characters;
34     }

 

  2.实现统计单词总数

  说明:因为单词可以有任意长度,并且单词可在注释中存在,故任何连续的字母串都将被认为是单词

 1 public static int wordcount(String filename){
 2         //统计单词数
 3         int words=0;
 4         String str = null;
 5         BufferedReader br = null;
 6         try {
 7             br = new BufferedReader(new FileReader(filename));
 8             Pattern pattern = Pattern.compile("[a-zA-Z]+");
 9             while((str=br.readLine())!=null){
10                 Matcher matcher = pattern.matcher(str);
11                 while(matcher.find()){
12                     words++;
13                 }
14             }
15 
16         } catch (Exception e) {
17             e.printStackTrace();
18         }finally {
19             if(br!=null){
20                 try {
21                     br.close();
22                 } catch (IOException e) {
23                     e.printStackTrace();
24                 }
25             }
26         }
27         return words;
28     }

 

  3.实现统计总行数

  说明:统计读取到的行数

 1 public static int linecount(String filename){
 2         //统计总行数
 3         int lines=0;
 4         File file = new File(filename);
 5         BufferedReader br = null;
 6         try {
 7             br = new BufferedReader(new FileReader(file));
 8             while(br.readLine() != null) {
 9                 lines++;
10             }
11 
12         } catch (Exception e) {
13             e.printStackTrace();
14         }finally{
15             if(br!=null){
16                 try {
17                     br.close();
18                 } catch (IOException e) {
19                     e.printStackTrace();
20                 }
21             }
22         }
23         return lines;
24     }

 

  4.为特殊行封装一个类

  说明:这样可以在统计特殊行时可以返回多个值

 1 public class Special {      //为特殊行封装一个类,以便于返回特殊行时能返回多个值
 2     private int nulllines=0;
 3     private int codelines=0;
 4     private int commentlines=0;
 5 
 6     public int getNulllines() {
 7         return nulllines;
 8     }
 9 
10     public int getCodelines() {
11         return codelines;
12     }
13 
14     public int getCommentlines() {
15         return commentlines;
16     }
17 
18     public void setNulllines(int nulllines) {
19         this.nulllines = nulllines;
20     }
21 
22     public void setCodelines(int codelines) {
23         this.codelines = codelines;
24     }
25 
26     public void setCommentlines(int commentlines) {
27         this.commentlines = commentlines;
28     }
29 }

 

  5.实现统计特殊行(空行、代码行、注释行)

  说明:

    空行----该行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”和“}”

    代码行----本行包括多于一个字符的代码

    注释行----本行不是代码行,并且本行包括注释,或者是} //注释等情况

 1 public static Special speciallinecount (String filename){
 2         int nulllines=0;
 3         int codelines=0;
 4         int commentlines=0;
 5         Special sp = new Special();
 6         String str = null;
 7         File file = new File(filename);
 8         BufferedReader br = null;
 9         try {
10             br = new BufferedReader(new FileReader(file));
11             while((str=br.readLine())!= null) {
12                if(str.length()==0){
13                    nulllines++;
14                }else{
15                    String substr = str.trim();  //去除读到的每行字符串的首尾空格
16                    if(substr.equals("{") || substr.equals("}")){
17                        nulllines++;
18                    } else if(substr.indexOf("//")==0 || substr.indexOf("}//")==0 || substr.indexOf("{//")==0){
19                        commentlines++;
20                    } else if(substr.indexOf("/*")==0 || substr.indexOf("}/*")==0 || substr.indexOf("{/*")==0){
21                        commentlines++;
22                        while((str=br.readLine())!= null){
23                            commentlines++;
24                            if(str.trim().contains("*/"))
25                                break;
26                        }
27                    } else{
28                         codelines++;
29                    }
30                }
31             }
32             sp.setNulllines(nulllines);
33             sp.setCodelines(codelines);
34             sp.setCommentlines(commentlines);
35         } catch (Exception e) {
36             e.printStackTrace();
37         }finally{
38             if(br!=null){
39                 try {
40                     br.close();
41                 } catch (IOException e) {
42                     e.printStackTrace();
43                 }
44             }
45         }
46         return sp;
47     }

 

  6.递归遍历文件夹/文件,输出文件的信息

  说明:getFileList方法实现了递归将文件夹中的文件存放在List<String>中,traverse方法实现了将获取到的文件名List逐个进行操作并输出对应的文件信息

 1 public static void traverse(String filename) {
 2         List<String> fileList = new ArrayList<>();
 3         fileList = getFileList(filename);
 4         for (String file : fileList) {
 5             int characters = 0;
 6             int words = 0;
 7             int lines = 0;
 8             int nulllines = 0;
 9             int codelines = 0;
10             int commentlines = 0;
11             Special sp = new Special();
12             characters = wordcounter.charcount(file);
13             words = wordcounter.wordcount(file);
14             lines = wordcounter.linecount(file);
15             sp = wordcounter.speciallinecount(file);
16             nulllines = sp.getNulllines();
17             codelines = sp.getCodelines();
18             commentlines = sp.getCommentlines();
19 
20             System.out.println("文件" + file + "的信息如下:");
21             System.out.println("字符总数:" + characters);
22             System.out.println("单词总数:" + words);
23             System.out.println("总行数:" + lines);
24             System.out.println("空行数:" + nulllines);
25             System.out.println("代码总行数:" + codelines);
26             System.out.println("注释总行数:" + commentlines);
27             System.out.println();
28         }
29     }
30         public  static List<String> fileList = new ArrayList<>();   //构建一个全局变量的list来装递归遍历的文件/文件夹
31 
32         public  static List<String> getFileList(String path){  //递归获取文件路径并装入List
33             File filepath = new File(path);
34             File [] files = filepath.listFiles();
35             if(files!=null){
36                 for(int i=0;i<files.length;i++){
37                     if(files[i].isDirectory()){
38                     getFileList(files[i].getAbsolutePath());
39                     }else if(files[i].isFile() && files[i].canRead()){
40                         String strfilename = files[i].getAbsolutePath();
41                         fileList.add(strfilename);
42                     }else{
43                         continue;
44                     }
45                 }
46             }
47             return fileList;
48         }

 

五、测试运行截图

  1.对单个文件进行基本功能测试  

     

        

                 

 

  2.进行图形界面选取文件,并显示文件的信息

       

       

      

 

   3.递归遍历F盘下的123文件夹

      

      

      

 

六、遇到的困难及解决方法

  • 遇到的问题:

  1.使用java设计程序,对java的基础知识不太熟悉

    解决办法:边做边查边学。

    收获:了解部分Java知识并加以应用。

  2.遇到GBK编码的源文件统计字符时字符个数不正确。

    解决办法:将源文件的编码格式改成UTF-8就能正确统计字符数了。

    收获:了解到UTF-8格式和GBK格式的编码字符数不是一样的。

  3.遇到递归遍历文件夹时只能读到第一层文件

    解决办法:经过查资料和调试,创建一个全局的list来装递归遍历的文件/文件夹就解决了。

 

七、PSP

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning 计划    
· Estimate · 估计这个任务需要多少时间  900 1083
Development 开发    
· Analysis · 需求分析 (包括学习新技术)  60  100
· Design Spec · 生成设计文档  10  8
· Design Review · 设计复审 (和同事审核设计文档)  10  2
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)  20  30
· Design · 具体设计  100  90
· Coding · 具体编码  500  570
· Code Review · 代码复审  60  60
· Test · 测试(自我测试,修改代码,提交修改)  120  130
Reporting 报告    
· Test Report · 测试报告  60  60
· Size Measurement · 计算工作量  10  8
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划  30  25
  合计  1880  2166

 

八:小结

  在实现这个项目过程中,通过不断摸索,查资料,逐步了解了一点Java知识;设计前的预估与设计时的实际情况还是有差别的,时间总是要预留多一点;在设计构建过程中,认识到构建一个项目,做好计划是项目的第一步,没有计划的项目是不知道从哪里开始的。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!