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的正则表达式
  • 相关阅读:
    从零开始入门 K8s | 应用编排与管理
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    203. Remove Linked List Elements
    183. Customers Who Never Order
    182. Duplicate Emails
    181. Employees Earning More Than Their Managers
    1261. Find Elements in a Contaminated Binary Tree
    1260. Shift 2D Grid
  • 原文地址:https://www.cnblogs.com/Allforward/p/12543453.html
Copyright © 2011-2022 走看看