软工作业-个人项目

github项目地址 https://github.com/HHTupup/WordCount.git

项目相关要求

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

具体功能要求:
程序处理用户需求的模式为:

wc.exe [parameter] [file_name]

基本功能表

指令 功能描述 是否实现
-c 返回文件的字符数
-w 返回文件的词的数
-l 返回文件的行数
-a 返回更复杂的数据(代码行 / 空行 / 注释行)
-s 递归处理目录下符合条件的文件
支持通配符
-x 显示图形界面

PSP

*PSP2.1* *Personal Software Process Stages* *预估耗时(分钟)* *实际耗时(分钟)*
Planning 计划 30 20
·Estimate · 估计这个任务需要多少时间 30 20
Development 开发 620 1000
· Analysis · 需求分析 (包括学习新技术) 60 330
· Design Spec · 生成设计文档 30 60
·Design Review · 设计复审 (和同事审核设计文档) 15 10
·Coding Standard · 代码规范 (为目前的开发制定合适的规范) 5 5
· Design · 具体设计 60 60
· Coding · 具体编码 300 190
· Code Review · 代码复审 30 40
· Test · 测试(自我测试,修改代码,提交修改) 120 300
Reporting 报告 70 60
· Test Report · 测试报告 20 15
· Size Measurement · 计算工作量 30 15
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 30
合计 720 1080

遇到的困难以及解决办法

  • 对编程环境不熟悉
    • 这次项目我使用我之前很少接触的JAVA实现,开发过程发现它有许多强大的类,然而我还是有点难以驾驭。
    • 开始想使用比较熟悉的c语言,但是觉得代码比较难管理,而且文件操作不如JAVA方便,JAVA有更严格的代码审查,能让我更好管理、检查代码。
    • 解决方案:使用万能的搜索引擎,再加上自己的摸索去熟悉JAVA。

关键代码及其说明

WordCount实现类

WordCount类是项目的核心,它实现了wc.exe的字符数统计、单词数统计、行数统计以及其他扩展功能。实现这些功能的关键是File类的方法以及String类的方法。

import java.io.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class WordCounter {

    //字符数统计
    static void CharCounter(String path) throws IOException {
        FileReader fr = new FileReader(path);
        BufferedReader br = new BufferedReader(fr);
        int ch = br.read();
        int count = 0;
        while(ch != -1) {
            if(ch!=' ' && ch!='\n' &&ch!='\r' && ch!='\t') count++;
            ch = br.read();
        }
        br.close();
        System.out.println("字符数:"+count);
    }


    //单词数统计
    static void WordCounter(String path) throws IOException {
        FileReader fr = new FileReader(path);
        BufferedReader br = new BufferedReader(fr);

        //用正则表达式匹配 由英文字母组成的子字符串 的个数
        String pattern = "[a-zA-Z]+";
        Pattern r = Pattern.compile(pattern);
        String line = br.readLine();
        int count = 0;
        while(line != null) {
            Matcher m = r.matcher(line);
            while(m.find()) {
                count++;
            }
            line = br.readLine();
        }

        br.close();
        System.out.println("单词数:"+count);
    }

    //行数统计
    static void LineCounter(String path) throws IOException {
        FileReader fr = new FileReader(path);
        BufferedReader br = new BufferedReader(fr);
        String line = br.readLine();
        int count = 0;
        while(line != null) {
            count++;
            line = br.readLine();
        }
        br.close();
        System.out.println("行数:"+count);
    }

    //空行、代码行、注释行统计
    static void ComplexLineCounter(String path) throws IOException {
        FileReader fr = new FileReader(path);
        BufferedReader br = new BufferedReader(fr);

        int CommentLineCount = 0, BlankLineCount = 0, CodingLineCount = 0;

        boolean in_block = false;

        String line = br.readLine();

        while(line != null) {
            line = line.trim();
            if(line.contains("/*")) in_block = true;

            //单行注释 或者 在区块注释内
            if(line.matches("^[{}]?//[^\\n]*") || in_block) {
                CommentLineCount++;
            }

            //空白行
            else if(line.matches("^[\\\\s]*$|^[{}();]$")) {
                BlankLineCount++;
            }

            //其余的是代码行
            else {
                CodingLineCount++;
            }

            if(line.contains("*/")) in_block = false;
            line = br.readLine();
        }
        br.close();
        System.out.println("代码行数:"+ CodingLineCount);
        System.out.println("空白行数:"+ BlankLineCount);
        System.out.println("注释行数:"+ CommentLineCount);
    }

    //分配指令并执行
    static void batch(String path, List commands) throws IOException {
        File file = new File(path);
        System.out.println(file.getPath());
        for(String item : commands) {
            switch (item) {
                case "-s": break;
                case "-a": ComplexLineCounter(path); break;
                case "-c": CharCounter(path); break;
                case "-w": WordCounter(path); break;
                case "-l": LineCounter(path); break;
                default: return;
            }
        }
        System.out.println("");
    }

    //从指定文件夹开始 递归批量处理文件
    static void TraverseFiles(String path, List commands, Pattern regexp) 
        throws IOException {
        
        File file = new File(path);

        //项目限定的文件类型.c .h .cpp 和 .txt 
        String type = "[.](c|h|txt|cpp)$";

        Pattern pattern = Pattern.compile(type);
        if(file.exists()) {//判断文件夹路径是否正确
            File[] files = file.listFiles();
            //如果文件夹为空 或者 这是个文件
            if(null == files || files.length == 0) {
                return;
            }
            //如果这是个文件夹
            else {
                //遍历子文件
                for(File fchild : files) {
                    //如果是文件夹,获取该文件夹路径,进入文件夹,递归调用
                    if(fchild.isDirectory()) {
                        TraverseFiles(fchild.getAbsolutePath(), commands, regexp);
                    }
                    //如果是文件,并且是.c/ .cpp/ .h/ .txt文件,则查看
                    else if(fchild.isFile() && pattern.matcher(fchild.getName()).find()
                            && regexp.matcher(fchild.getName()).find()){

                        batch(fchild.getAbsolutePath(), commands);
                    }
                }
            }
            
        }
        
        else {
            System.out.println(path+":文件不存在");
        }
    }
}

主函数

导图分析:
软工作业-个人项目_第1张图片

接收参数并分配执行,但是我对JAVA的认识还不够,代码写得不太好,最终if,else用得有点多。

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

public class app {
    public static void main(String[] args) throws IOException {

        //默认路径是可执行文件当前路径
        String path = "./";

        //表示是否有从参数中收集到路径,最多只接收一次
        boolean received = false;

        //收集到的指令集合
        List command = new ArrayList<>();

        //开始收集指令和路径
        for(String item : args) {

            //符合指令的格式 并且 不收集重复的指令
            if(item.matches("^-.*")) {
                if(item.matches(("^-[awslc]$")) && !command.contains(item)) {
                    command.add(item);
                }
                else {
                    System.out.println("错误:指令'"+item+"'无法识别");
                    return;
                }
            }

            //其余格式被认为是文件路径
            else if(!received){
                received = true;
                path = item;
            }

            //输入了多个路径,要提示错误,程序结束
            else {
                System.out.println("错误:最多只能输入一个路径");
                return;
            }
        }


        Pattern pattern = Pattern.compile(".*");

        //收集结束后, 对收集数据进行分析
        //含有 '*', '?'  被认为是通配符模式,从当前目录开始递归查找
        if(path.matches(".*[*?].*")) {
            path = path.replace("*",".*");
            path = path.replace("?",".?");
            pattern = Pattern.compile(path);
            WordCounter.TraverseFiles("./", command, pattern);
        }

        //其余被认为是正常文件路径 即相对路径或绝对路径
        else {
            File file = new File(path);
            //检查路径是否正确,正确则执行指令
            if(file.exists()){
                //指定目录是文件夹
                if(file.isDirectory()) {
                    if(command.contains("-s")) {
                        WordCounter.TraverseFiles(path, command, pattern);
                    }
                    else {
                        System.out.println("错误:指定目录是文件夹,请先使用-s 指令查看该目录下适用的文件");
                        return;
                    }
                }
                //指定目录是文件
                else {
                    WordCounter.batch(path, command);
                }
            }
            //不正确路径提示错误,程序结束
            else {
                System.out.println("错误:未找到指定文件或文件夹");
                return;
            }
        }

    }
}

运行测试

  • 测试目录结构
    软工作业-个人项目_第2张图片

  • 正常功能测试
    软工作业-个人项目_第3张图片
    软工作业-个人项目_第4张图片

  • 不存在的文件路径

  • 递归处理测试
    软工作业-个人项目_第5张图片

  • 支持通配符的功能测试

  • 输入未知的指令

项目小结

  • 对JAVA有更进一步了解,与C语言相比,JAVA的编程更像是在“拼接组件”。
  • 编码时间比预计要短,是因为设计阶段花了更多的时间。下手写代码前先做好设计流程,有一个逻辑清晰的流程图能够有效提高编码效率,降低出现bug的频率。
  • 代码写得不是很规范,可读性和扩展性稍差。因为是个人项目就没太注意规划。

你可能感兴趣的:(软工作业-个人项目)