zoukankan      html  css  js  c++  java
  • 个人项目(Word Count)

    一、Github项目地址

      https://github.com/AllForward/GP_Homework/tree/master/个人项目


    二、题目叙述

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

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

      2、基本功能列表:

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

      3、扩展功能:

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

      4、高级功能:

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

      5、注意事项:  

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

    三、解题思路

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

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

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

          


    四、设计实现过程

     整个程序分成三个类:

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

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

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

      流程图如下:

        


    五、代码说明

    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+,\.
    \;\(\<\[\{]");
            //对切割后的每个字符串进行判断
            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() + ":
    " +
                                "文件空行数:" + nullLine + "
    " +
                                "文件注释行数:" + notesLine + "
    " +
                                "代码行数:" + codeLine;
                    case "-x":
                        return file.getName() + ":
    " +
                                "文件总字符数:" + charNum + "
    " +
                                "文件的单词数:" + wordNum + "
    " +
                                "文件总行数:" + line + "
    " +
                                "文件空行数:" + nullLine + "
    " +
                                "文件注释行数:" + notesLine + "
    " +
                                "代码行数:" + 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, "文本内容统计:
    " + HandleFile.FileStatistics(file, "-x"));
                    }
                }
            });
        }
    }

    六、测试运行

    测试文件:

    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文件)


    七、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


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

     

     

    对文本通配符转换成java的正则表达式
  • 相关阅读:
    jquery index与eq
    尝试一下
    document
    2017-03-28 java script DOM操作
    2017-03-25 CSS 样式
    CSS 样式表分类
    CSS 样式表
    HTML 框架
    表格
    HTML常用标记
  • 原文地址:https://www.cnblogs.com/Allforward/p/12543453.html
Copyright © 2011-2022 走看看