我的项目(Word Count)

1、Github项目地址java

  https://github.com/AllForward/GP_Homework/tree/master/我的项目git


 

2、题目叙述github

  这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具有其余扩展功能,并可以快速地处理多个文件。正则表达式

  一、具体功能要求:
    程序处理用户需求的模式为:wc.exe [parameter] [file_name]编程

  二、基本功能列表:数组

  1. wc.exe -c file.c     //返回文件 file.c 的字符数
  2. wc.exe -w file.c    //返回文件 file.c 的词的数目  
  3. wc.exe -l file.c      //返回文件 file.c 的行数

  三、扩展功能:ide

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

  四、高级功能:函数

     -x 参数。这个参数单独使用。若是命令行有这个参数,则程序会显示图形界面,用户能够经过界面选取单个文件,程序就会显示文件的字符数、行数等所有统计信息。学习

  五、注意事项:  测试

  1. 空行:本行所有是空格或格式控制字符,若是包括代码,则只有不超过一个可显示的字符,例如“{”。
  2. 代码行:本行包括多于一个字符的代码。
  3. 注释行:本行不是代码行,而且本行包括注释。(注意:} //注释 算做是注释行)
  4. [file_name]: 文件或目录名,能够处理通常通配符。(通常通配符包括[],*,?,!,^)

3、解题思路

  一、首先题目要求说要对文本的内容进行一些统计,很显然须要采用IO流来对文件进行读取,以后对读取的内容进行逐一统计,因为本人对Java语言较为熟悉,因此决定采用Java来进行编程;

    二、对于文件内容的统计,Java有对应的API能够读取每一行的内容,那么我就对每次读到的一行的内容进行分析统计,具体分析统计过程以下:每次将读取的一行内容用字符串类型进行存储;

  • 对于字符数,我先将每一行内容中的空格去掉,以后再获取字符串的长度,这样就能够获得每一行的字符数,最后将每一行的字符数相加就为文件总的字符数;
  • 对于词的数量,则是采用正则表达式的方式将每一行读取到的内容,经过空格、分号、逗号、括号、点等标点符号进行分割成多个字符串,再经过正则将这多个字符串中非字母的字符去除,最终得到的字符串若不为空就是一个单词;(例子:读取到的行内容为:
    package com.company.FileStatistics; 那么通过分割后的多个字符串分别为:package,com,company,FileStatistics。因为这些字符串中的不存在非字母的字符,因此最终结果就是这4个单词;)最后将每一行的单词数相加就为文件总的单词数;
  • 对于文件行数,那么就是经过循环读取文件的每一行,每读一行总行数+1,直到读完整个文件;
  • 对于递归处理目录下符合条件的文件,则是经过文件名通配符对当前路径下的全部文件进行扫描,一旦有符合的文件名,就将该文件加入到文件数组中,最后经过循环对文件数组的中每个文件进行统计分析;
  • 对于更复杂的数据(代码行 / 空行 / 注释行),首先是空行,那么每当读取到的内容为空,或者改行只有一个字符,例如{,},那就是空行;注释行则是要有/**/,//,我经过正则表达式的方式来判断该行是否为注释行,其中须要注意到/**/形式的注释能够跨越多行,以及相似于"{ //"的注释行;代码行的话,则是须要该行在除去注释内容的基础上,剩余的字符数多于1个;
  • 最后是GUI界面,我是经过Java的图形库JavaJW来实现的,主要是经过设置一个文件选择框,来让用户选择文件,以后返回文件的统计结果;

      


4、设计实现过程

 整个程序分红三个类:

  主类:Main,普通及扩展功能HandleFile类以及GUI图形界面类Swing

 

 整个程序分红几个主要函数:

  1. FileHandle(String[] args)接收控制台输入,同时判断是否要进行GUI界面来统计,不是则调用ispermit函数判断是否为能够进行统计的文件类型,是则调用change(String matches)对用户输入的文本通配符转换成java正则表达式再调用getFiles()函数获取符合条件的文件,是单个仍是多个;
  2. FileStatistics(File file, String type) 获取用户想要统计的类型,是空行仍是单词数等等;
  3. SwingStatistics()经过GUI图形界面进行统计的函数等等统计函数;

  流程图以下:

    


 

 

5、代码说明

1.Main类

package com.company; import com.company.FileStatistics.HandleFile; public class Main { public static void main(String[] args) { HandleFile.FileHandle(args); } }

2. HandleFile类

package com.company.FileStatistics; import java.io.*; import java.util.ArrayList; import java.util.List; /** * @description 文件基础以及扩展功能实现 * @author guopei * @date 2020-03-13 10:55 */

public class HandleFile { //当前exe所在的文件路径
    public static final String address = System.getProperty("exe.path"); public static Boolean isNotes = false; public static void FileHandle(String[] args) { // System.out.println("当前路径为:" + address);
        if (args[0].equals("-x")) { Swing.SwingStatistics(); return; } List<File> fileList = new ArrayList<File>(); //对发送过来的命令进行判断
        if (isPermit(args[args.length - 1])) { File file = new File(address); args[args.length - 1] = change(args[args.length - 1]); if (file.isDirectory()) { //若是是文件夹,获取当下的符合条件的文件
                getFiles(fileList, file.getPath(), args[args.length - 1]); } else { if (file.getName().matches(args[args.length - 1])) { fileList.add(file); } } //命令是递归查询多个文件
            if (args[0].equals("-s")) { if (fileList.size() < 1) { System.out.println("查找不到符合条件的文件"); return; } //说明要统计多个文件
                for (File f : fileList) { String response = FileStatistics(f, args[1]); if ("不支持的命令格式".equals(response)) { System.out.println(response); break; } else if (response != null) { System.out.println(response); } } } //命令是查询单个文件
            else { if (fileList.size() < 1) { System.out.println("查找不到符合条件的文件"); return; } else if (fileList.size() > 1) { System.out.println("查找到不止一个符合条件的文件,请加入-s命令来递归处理多个文件"); return; } System.out.println(FileStatistics(fileList.get(0), args[0])); } } else { System.out.println("文件格式不支持"); } } private static void getFiles(List<File> fileList, String path, String matches) { File file = new File(path); File[] files = file.listFiles(); for(File fileIndex:files){ //若是这个文件是目录,则进行递归搜索
            if(fileIndex.isDirectory()){ getFiles(fileList, fileIndex.getPath(), matches); }else { //若是文件是符合匹配条件的文件,则将文件放入集合中
                if (fileIndex.getName().matches(matches)) { fileList.add(fileIndex); } } } } private static Integer WordStatistics(String content) { //统计词的数目
        String copy = content; Integer num = 0; //统计词的个数,将每一行数据切割成多个子字符串
        String[] strings = copy.split("[\\s+,\\.\n\\;\\(\\<\\[\\{]"); //对切割后的每一个字符串进行判断
        for (int i = 0; i < strings.length; i++) { //将字符不是字母的去除
            strings[i] = strings[i].replaceAll("[^a-zA-Z]", ""); if (!strings[i].equals("")) { //若是不是空行,单词数+1
                num++; } } return num; } //对文本通配符转换成java的正则表达式
    public static String change(String matches) { String[] split = matches.split("\\."); matches = matches.replace(".", ""); matches = matches.replace(split[split.length - 1], "(." + split[split.length - 1] + ")"); matches = matches.replaceAll("\\?", ".{1}"); matches = matches.replaceAll("\\*", ".+"); matches = matches.replaceAll("!", "^"); return matches; } //统计更复杂的数据(代码行 / 空行 / 注释行)
    public static Integer[] LineStatistics(String content) { //顺序为:空行 / 注释行
        Integer[] lines = {0, 0, 0}; //统计空行数
        if (content.equals("") || content.matches("[\\{\\}]")) { lines[0]++; } //统计注释行
        else  if (content.matches("(.*)\\/\\/(.*)")) { lines[1]++; } else if (content.matches(".*\\/\\*.*\\*\\/")) { //说明使用了/**/的注释形式
            lines[1]++; } else if (content.matches("(.*)\\/\\*(.*)")) { //说明使用了/*的注释形式
            lines[1]++; isNotes = true; } else if (content.matches("(.*)\\*\\/(.*)")) { lines[1]++; isNotes = false; } else if (isNotes == true) { lines[1]++; } //统计代码行
        if (content.matches(".{2,}") && isNotes == false && content.matches("(.*)[^\\\\/\\\\*](.*)")) { //去除掉注释部分任然含有两个及以上字符则就是代码行
            String copy = content; copy = copy.replaceAll("\\/\\*.*\\*\\/", ""); copy = copy.replaceAll("\\/\\/(.*)", "").trim(); if (copy.matches(".{2,}")) { lines[2]++; } } return lines; } //正则判断文件格式是否支持进行统计(符合的标准为.c,.java,.cpp,.py,.txt)
    private static Boolean isPermit(String fileType) { if (fileType.matches(".+(.c|.java|.cpp|.py|.txt)$")) { return true; } return false; } /** * wc.exe -c file.c //返回文件 file.c 的字符数 * * wc.exe -w file.c //返回文件 file.c 的词的数目 * * wc.exe -l file.c //返回文件 file.c 的行数 * 扩展功能: * -s 递归处理目录下符合条件的文件。 * -a 返回更复杂的数据(代码行 / 空行 / 注释行)。 * @param file * @param type * @return
     */
    public static String FileStatistics(File file, String type) { //行数
        Integer line = 0; //单词数
        Integer wordNum = 0; //字符数
        Integer charNum = 0; //空行数
        Integer nullLine = 0; //注释行
        Integer notesLine = 0; //代码行
        Integer codeLine = 0; try { //判断文件类型是否符合
            if (!isPermit(file.getName())) { System.out.println("不支持的文件格式"); return "不支持的文件格式"; } BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); String content = null; Boolean isNotes = false; while ((content = bufferedReader.readLine()) != null) { //去除字符串的首尾空格
                content = content.trim(); //System.out.println(content);
                switch (type) { case "-c": //返回文件的字符数 //将空格所有去除
                        content = content.replaceAll(" +", ""); charNum += content.length(); break; case "-w": //统计词的数目
                        wordNum += WordStatistics(content); break; case "-a": Integer[] lines = LineStatistics(content); nullLine += lines[0]; notesLine += lines[1]; codeLine += lines[2]; break; case "-x": //统计所有信息
                        content = content.replaceAll(" +", ""); charNum += content.length(); wordNum += WordStatistics(content); Integer[] Lines = LineStatistics(content); nullLine += Lines[0]; notesLine += Lines[1]; codeLine += Lines[2]; break; case "-l": break; default: return "不支持的命令格式"; } //总行数
                line++; } switch (type) { case "-c": return file.getName() + "文件总字符数:" + charNum; case "-w": return file.getName() + "文件的单词数:" + wordNum; case "-l": return file.getName() + "文件总行数:" + line; case "-a": return file.getName() + ":\r\n" +
                            "文件空行数:" + nullLine + "\r\n" +
                            "文件注释行数:" + notesLine + "\r\n" +
                            "代码行数:" + codeLine; case "-x": return file.getName() + ":\r\n" +
                            "文件总字符数:" + charNum + "\r\n" +
                            "文件的单词数:" + wordNum + "\r\n" +
                            "文件总行数:" + line + "\r\n" +
                            "文件空行数:" + nullLine + "\r\n" +
                            "文件注释行数:" + notesLine + "\r\n" +
                            "代码行数:" + codeLine; default: return "不支持的命令格式"; } } catch (FileNotFoundException e) { System.out.println("该文件或文件名不存在"); } catch (IOException e) { System.out.println("文件读取错误"); } return null; } }

3.Swing类

 

package com.company.FileStatistics; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; /** * @description 图形界面统计 * @author guopei * @date 2020-03-15 17:50 */
public class Swing { public static void SwingStatistics() { JFrame frame = new JFrame("文件选择"); frame.setLayout(new FlowLayout()); JFileChooser chooser = new JFileChooser(); JButton bOpen = new JButton("打开文件"); frame.add(bOpen); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(250, 150); frame.setLocationRelativeTo(null); frame.setVisible(true); bOpen.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int returnVal = chooser.showOpenDialog(frame); File file = chooser.getSelectedFile(); if (returnVal == JFileChooser.APPROVE_OPTION) { JOptionPane.showMessageDialog(frame, "计划打开文件:" + file.getAbsolutePath()); JOptionPane.showMessageDialog(frame, "文本内容统计:\n" + HandleFile.FileStatistics(file, "-x")); } } }); } }

 


6、测试运行

测试文件:

1.cpp(空文件) 

2.cpp(内容以下图所示)

 

 

测试效果:

1.统计空文件1.cpp效果图:(其中包括-w,-c以及-l命令)

 

 

2.统计非空文件2.cpp效果图:(其中包括-a,-w,-c以及-l命令)

3.统计多个文件效果图:(其中包括-a命令)

匹配全部.cpp文件

 

 

 匹配?.cpp文件

 

 

 匹配非1.cpp的文件

 

 

4. GUI界面统计效果图:

输入FileStatistics.exe -x后按回车便会有GUI图形界面提示

 

 

 

 点击"打开文件"按钮后的效果以下:

 

 

从其中选择一个符合统计条件的文件进行统计:

 

 

若选择了不能统计的文件,则会有相应的提示,效果以下:(例如选择了一个exe文件)


 

7、PSP表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 15

 20

· Estimate

· 估计这个任务须要多少时间

 180

 200

Development

开发

 130

 140

· Analysis

· 需求分析 (包括学习新技术)

 10

 10

· Design Spec

· 生成设计文档

 50

 60

· Design Review

· 设计复审 (和同事审核设计文档)

 10

 10

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 8

 10

· Design

· 具体设计

 20

 25

· Coding

· 具体编码

 130

 140

· Code Review

· 代码复审

 20

 25

· Test

· 测试(自我测试,修改代码,提交修改)

 30

 30

Reporting

报告

10

10 

· Test Report

· 测试报告

10 

10 

· Size Measurement

· 计算工做量

10 

20 

· Postmortem & Process Improvement Plan

· 过后总结, 并提出过程改进计划

10

 10

合计

 

 250

 260

 


 

8、项目小结
  本次项目让我对于Java的IO流以及正则表达式的相关知识有了进一步地巩固,同时经过PSP表格能够发现,我的实际的开发时间是比预计的时间长的,主要的缘由是在一些知识点上还不够熟悉,须要经过查阅相关资料以及笔记才能得以解决,同时在编写代码的时候.,有一些地方没有注意,致使在测试的时候出现bug,所以返回去修改bug消耗的时间也是比较长的.总的来讲就是分析问题须要再细致一些,对于一些比较特殊的状况要进行综合考虑分析,还有就是知识点须要不断地复习!

 

 

 

 

对文本通配符转换成java的正则表达式
相关文章
相关标签/搜索