• wordcount


    Github项目地址:https://github.com/454469625/SoftwareEngineering

    题目描述

    Word Count
    1. 实现一个简单而完整的软件工具(源程序特征统计程序)。
    2. 进行单元测试、回归测试、效能测试,在实现上述程序的过程中使用相关的工具。
    3. 进行个人软件过程(PSP)的实践,逐步记录自己在每个软件工程环节花费的时间。

    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   返回更复杂的数据(代码行 / 空行 / 注释行)。(引用他人博客并加以理解,非自己实现)

        支持各种文件的通配符(*,?)(同引用他人博客,未能自己实现)

    高级功能:

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

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


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

    解题思路:

      1.学习java的文件IO以及与String相关的java类的使用

      2.学习正则表达式,使用正则表达式来匹配空格,注释符号等

      3.学习java文件转为exe的方法

    主要代码:

    1.main函数

    package cn.lu.wordcount;
    
    import cn.lu.wordcount.CountUtils;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.Scanner;
    public class Main{
            private static boolean isCountchar = false;
            private static boolean isCountword = false;
            private static boolean isCountline = false;
            private static boolean isRecursion = false;
            private static boolean isCountdiffline = false;
            //利用arraylist存储符合条件的文件的绝对路径
            private static ArrayList<String> fileList = new ArrayList<String>();
            private static Scanner scanner;
    
            public static void main(String[] args) throws IOException,ArrayIndexOutOfBoundsException{
    
                /*此处在exe中测试出错,因此改用另一种方法,用scanner
    
    
                //此处args似乎越界了也许我应该给它分配一个空间?
                //args = new String[2];
                //运行报错了但是测试可以通过
                //默认最后一个参数为路径名
                String path = args[args.length-1];
                CountUtils countUtils = new CountUtils();
                //判断需要执行的功能
                for(int i=0; i<args.length-1; i++){
                    if(args[i].equals("-c")){
                        isCountchar = true;
                    }
                    if(args[i].equals("-w")){
                        isCountword = true;
                    }
                    if(args[i].equals("-l")){
                        isCountline = true;
                    }
                    if(args[i].equals("-s")){
                        isRecursion = true;
                    }
                    if(args[i].equals("-a")){
                        isCountdiffline = true;
                    }
                }
    
    
                */
                //方法二,用scanner获取输入
                //switch匹配字符串好像只在1.7版本以上才可以
                CountUtils countUtils = new CountUtils();
                String opera = null;
                String path = null;
                scanner = new Scanner(System.in);
                System.out.println("请输入你要执行的操作:");
                opera = scanner.nextLine();
                System.out.println("请输入你要操作的文件绝对路径:");
                path = scanner.nextLine();
                switch (opera) {
                    case "-c":
                        isCountchar = true;
                        break;
                    case "-w":
                        isCountword = true;
                        break;
                    case "-l":
                        isCountline = true;
                        break;
                    case "-s":
                        isRecursion = true;
                        break;
                    case "-a":
                        isCountdiffline = true;
                        break;
                    default:
                        System.out.println("操作不存在.");
                }
                //获取目录名
                //split 里面的参数是正则表达式,在Java里面用字符串表示正则表达式时,
                //反斜杠是转义符,表示一个反斜杠时,要在前面加一个斜杠,即 \ 表示一个斜杠。
                //所以此处split中的\\表示\用于分割文件路径
                //此处的一系列操作是为了获取所给文件路径的目录,即将路径最后的文件名删去
                String paths[] = path.split("\\");
                StringBuilder sb = new StringBuilder();
                for(int i=0; i<paths.length-1; i++){
                    if(i==paths.length-2){
                        sb.append(paths[i]);
                    }else{
                        sb.append(paths[i] + "\");
                    }
                }
    
                String dirName = sb.toString();
                //File(String pathname)通过给定路径名字符串转换为抽象路径名来创建一个新File实例
                File file = new File(dirName);
                if (!file.isDirectory()){
                    System.out.println("路径错误!");
                }
                String fileName = paths[paths.length - 1];
                //对文件名通配符处理
                //replaceAll() 方法使用给定的参数 replacement 替换字符串所有匹配给定的正则表达式的子字符串。
                //在 Java中,\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义
                fileName = fileName.replaceAll("\*","\.+").replaceAll("\?","\.");
    
                //若IS_RECURSION,则使用递归获取文件名(包括子目录),否则只获取当前目录下符合条件的文件名
                if (isRecursion){
                    countUtils.getRecursionFiles(dirName, fileName);
                }else{
                    countUtils.getFiles(dirName,fileName);
                }
                fileList = countUtils.fileList;
    
                //遍历fileList,对每一个文件使用选择的功能
                for (String fList :
                        fileList) {
                    System.out.println("文件路径为:"+fList);
                    if (isCountchar){
                        countUtils.countChar(fList);
                    }
                    if (isCountword){
                        countUtils.countWord(fList);
                    }
                    if (isCountline){
                        countUtils.countLine(fList);
                    }
                    if (isCountdiffline){
                        countUtils.countDiffline(fList);
                    }
                }
            }
    }

    2.工具类

    package cn.lu.wordcount;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * 部分引用:https://www.cnblogs.com/yw0219/p/8047938.htmlhttps://www.cnblogs.com/happyOwen/p/9646411.html
     *
     */
    
    public class CountUtils {
    
        //实现-c功能 利用BufferedReader整行读取统计字符数
        public int countChar(String path){
            File file = new File(path);
            BufferedReader br = null;
            String line;
            int charCount = 0;
            try {
                br = new BufferedReader(new FileReader(file));
                while ((line = br.readLine()) != null) {
                    char[] chars = line.toCharArray();
                    for (int i = 0; i < chars.length; i++) {
                        //Character 类用于对单个字符进行操作
                        //若不是空格,则字符数加1
                        if (!Character.isWhitespace(chars[i])) {
                            charCount++;
                        }
                    }
                }
                System.out.println("the charCount is:" + charCount);
                br.close();
            } catch (Exception e) {
                System.out.println(path+"文件名错误");
            }
            return charCount;
        }
    
    
        //实现-w功能
        //统计英文单词数
        //先用Bufferedreader整行读取,然后添加到StringBuffer中,
        //将StringBuffer转化为字符串后,然后将非英文字符替换为空格,再根据空格分割
        public int countWord(String path) {
            BufferedReader br = null;
            String line;
            String[] strings;
            StringBuffer sbf = new StringBuffer();
            int wordCount = 0;
            String reg = "\s+";
            try {
                br = new BufferedReader(new FileReader(path));
                while ((line = br.readLine()) != null) {
                    sbf.append(line);
                }
                //将不是英文字母的字符替换成空格
                String s = sbf.toString().replaceAll("[^A-Za-z]", " ");
                strings = s.split(reg);
                wordCount = strings.length;
                System.out.println("the wordcount is;"+wordCount);
                br.close();
            }catch (Exception e){
                System.out.println(path+"文件名错误");
            }
            return wordCount;
        }
    
        //-l    统计总行数
        public int countLine(String path) {
            BufferedReader br = null;
            String line;
            int lineCount = 0;
            try {
                br = new BufferedReader(new FileReader(path));
                while ((line = br.readLine()) != null) {
                    lineCount++;
                }
                System.out.println("the lineCount is:"+lineCount);
                br.close();
            }catch (Exception e){
                System.out.println(path+"文件名错误");
            }
            return lineCount;
        }
    
        //-a    统计注释行、空行、代码行
        public void countDiffline(String path) {
            int annotationLineCount = 0;
            int codeLineCount = 0;
            int spaceLineCount = 0;
            String line;
            BufferedReader br = null;
            //注释匹配器(匹配单行、多行、文档注释)
            Pattern annotationLinePattern = Pattern.compile("(//)|(/\*)|(^\s*\*)|(^\s*\*+/)");
            try {
                br = new BufferedReader(new FileReader(path));
                while ((line = br.readLine()) != null) {
                    if (annotationLinePattern.matcher(line).find()) {
                        //注释行
                        annotationLineCount++;
                    } else if (line.matches("\s*\p{Graph}?\s*")) {
                        //空行
                        spaceLineCount++;
                    } else {
                        codeLineCount++;
                    }
                }
                System.out.println("the nullLineNum is:" + spaceLineCount);
                System.out.println("the annotationLineNum is:" + annotationLineCount);
                System.out.println("the codeLineNum is:" + codeLineCount);
                br.close();
            } catch (Exception e) {
                System.out.println(path+"文件名错误");
            }
        }
    
        //用ArrayList存储符合条件的文件的绝对路径
        public ArrayList<String> fileList = new ArrayList<String>();
    
        //递归获取符合条件的文件的绝对路径(在Main.java中已经对fileName进行通配符处理)
        public void getRecursionFiles(String dirName, String fileName) {
            File file = new File(dirName);
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                if (files!=null){
                    for (File f : files) {
                        //当file1仍然是目录时,递归调用此函数
                        if (f.isDirectory()) {
                            getRecursionFiles(dirName+"\"+f.getName(),fileName);
                        }else{
                            //处理后的fileName作为匹配规则,对遍历的文件进行匹配
                            //pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。
                            // 要创建一个 Pattern 对象,必须首先调用其公共静态编译方法,
                            // 它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
                            Pattern pattern = Pattern.compile(fileName);
                            //Matcher 对象是对输入字符串进行解释和匹配操作的引擎。Matcher 也没有公共构造方法。
                            // 需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象
                            Matcher m = pattern.matcher(f.getName());
                            if (m.matches()) {
                                fileList.add(dirName+"\"+f.getName());
                            }
                        }
                    }
                }
            }
        }
    
        //非递归获取符合条件的文件的绝对路径(在Main.java中已对fileName进行通配符处理)
        //仅获取当前目录下符合条件的文件,并将其绝对路径添加到ArrayList
        public void getFiles(String dirName, String fileName) {
            File file = new File(dirName);
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                if (files != null) {
                    for (File f : files) {
                        if (!f.isDirectory()) {
                            Pattern pattern = Pattern.compile(fileName);
                            Matcher m = pattern.matcher(f.getName());
                            if (m.matches()) {
                                fileList.add(dirName+"\"+f.getName());
                            }
                        }
                    }
                }
            }
        }
    }

     测试结果

    1.空文件

     2.典型的文件

    psp

    PSP2.1

    Personal Software Process Stages

    预估耗时(分钟)

    实际耗时(分钟)

    Planning

    计划

     30

     40

    · Estimate

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

    360

     480

    Development

    开发

     120

     150 

    · Analysis

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

    360

     420

    · Design Spec

    · 生成设计文档

     10

     10

    · Design Review

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

     10

     10

    · Coding Standard

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

     10

     30

    · Design

    · 具体设计

     40

     75

    · Coding

    · 具体编码

     100

    150

    · Code Review

    · 代码复审

     20

     30

    · Test

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

     30

     60

    Reporting

    报告

     25

     25

    · Test Report

    · 测试报告

     10

     10

    · Size Measurement

    · 计算工作量

     5

     5

    · Postmortem & Process Improvement Plan

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

     15

     30

    合计

    1055

    1525

    总结:由于实践次数少之又少,导致知识学了完全不会用,而且学的知识也有很大的漏洞,在实际设计的时候需要一直百度,即使自己明白怎么做,可是在开发的时候又有各种小问题,就连github的上传都要花半小时重新学习,希望以后自己能提高实践的次数,千里之行始于足下。

  • 相关阅读:
    Twitter注册
    iOS项目的完整重命名方法图文教程
    加载gif动态图的三种方式
    只 一行显示可左右滚动的文本(UITextField中文限制)
    iOS学习资料链接
    GCD常用方法
    移动端轮播完整版css3加原生写法
    zepto-touch.js插件
    移动端续讲及zepto移动端插件外加touch插件介绍
    解决ios和Android的差异
  • 原文地址:https://www.cnblogs.com/LuuCq/p/12561879.html
Copyright © 2020-2023  润新知