个人项目:实现wc.exe(Java)

时间:2021-09-18 04:33:47

本项目Github地址:https://github.com/NNewBoy/wc

项目相关要求

基本功能:(已实现)

  -c 统计文件字符数

  -w 统计文件词的数目

  -l 统计文件行数

扩展功能:(已实现)

  -s 递归处理目录下符合条件的文件

  -a 返回更复杂的数据(代码行 / 空行 / 注释行)

高级功能:(未实现)

   -x 显示图形界面

解题思路

  拿到项目后,首先理清项目需求,然后规划项目框架,根据框架设计功能函数,思考函数的实现方法,查找资料,本次项目查找了关于正则表达式的资料;设计部分完毕,开始代码编写,最后就是检测代码,修改代码。

设计实现过程

代码分成两大部分,5个类。

第一命令部分:Main类实现输入命令,Command类实现对命令的解析

输入命令后调用Command类解析命令,Command解析命令后调用功能类。

第二功能部分:Basicfunction类实现基本功能,Directory类实现-s功能,Increasedfunction类实现-a功能

基本功能通过正则表达式匹配每个字符实现;-s由于要递归目录里的多个文件,跟其他命令实现过程有所区别,因此分开编写,通过File类的listFiles()方法取得目录里符合条件的文件,然后各自调用Command类;-a也是使用正则表达式,统计代码行、空行、注释行,然后返回数目。

代码说明

命令解析:

public class Command {
    void command(String input,String filename,Matcher matcher) {
        try {
            Boolean s = false;
            List<String> commands = new ArrayList<>();
            while (matcher.find()) {
                //将接收到的命令存入动态数组中
                String command = matcher.group();
                //判断是否接收到-s
                if (command.equals("-s")) {
                    s = true;
                } else {
                    commands.add(command);
                }
            }
            //实现-s命令
            if (s) {
                new Directory().readDirectory(filename, input);
            } else {
                //实现其他命令
                System.out.println("文件:" + filename);
                for (String command1: commands) {
                    switch (command1) {
                        case "-c":
                            new Basicfunction().c(filename);
                            break;
                        case "-w":
                            new Basicfunction().w(filename);
                            break;
                        case "-l":
                            new Basicfunction().l(filename);
                            break;
                        case "-a":
                            new Increasedfunction().a(filename);
                            break;
                        default:
                            System.out.println("命令输入错误");
                            break;
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("输入路径出错");
        }
    }
}

对输入的命令,先判断是否有-s命令,是则调用Direction类遍历出符合条件的文件路径,否则当前路径就是目标文件的路径。

实现-s命令:

public class Directory {
    void readDirectory(String filename , String input) {
        Pattern pattern = Pattern.compile("\\-[cwla]");
        Matcher matcher = pattern.matcher(input);
        File filedirectory = new File(filename);
        File[] files = filedirectory.listFiles();
        for (File file : files) {
            //遍历路径里的文件夹
            if (file.isDirectory()) {
                readDirectory(file.getPath(),input);
            } else {
                //实现-c -w -l -a 命令
                new Command().command(input,file.getPath(),matcher);
            }
        }
    }
}

遍历出符合条件的文件路径,对每个符合条件的文件调用Command类。

实现-c -w -l 命令:

public class Basicfunction {
    private String s1;
    private String s2;
    private int ccount;
    private int wcount;
    private int lcount;

    private void read(String filename) throws IOException {
            BufferedReader brin = new BufferedReader(new FileReader(filename));
            while ((s1=brin.readLine())!=null) {
                ccount += s1.length();//统计字符数
                s1 = s1 + " ";
                s2 += s1;
                lcount ++;//统计行数
                Pattern pattern = Pattern.compile("\\w+");
                Matcher matcher = pattern.matcher(s1);
                while (matcher.find()) {
                    wcount++;//统计词数
                }
            }
            brin.close();
    }

    public void c(String filename) {
        try {
            read(filename);
            System.out.println("字符数:"+ccount);
        } catch (Exception e) {
            System.out.println("输入路径出错");
        }
    }

    public void w(String filename) {
        try {
            read(filename);
            System.out.println("词的数目:"+wcount);
        } catch (Exception e) {
            System.out.println("输入路径出错");
        }
    }

    public void l(String filename) {
        try {
            read(filename);
            System.out.println("行数:"+lcount);
        } catch (Exception e) {
            System.out.println("输入路径出错");
        }
    }
}

通过read()函数统计字符数、词数、行数,然后再返回。

实现-a命令:

public class Increasedfunction {
    private int emptyLine = 0,codeLine = 0,explainLine = 0,lineCount = 0;

    //通过正则表达式匹配指定字符
    private int match(String regex , String sentence) {
        int i = 0;
        Pattern pattern1 = Pattern.compile(regex);
        Matcher matcher1 = pattern1.matcher(sentence);
        while (matcher1.find()) {
            i++;
        }
        return i;
    }

    private void read(String filename) throws IOException {
        //空字符数
        int emptyCount;
        //各种标点符号数
        int emptyCharCount;
        //词数
        int displayCharCount;
        //单行注释符"//"
        int explainCount1;
        //多行注释开始"/*"
        int explainCount2;
        //多行注释结束"*/"
        int explainCount3;
        //含有注释符"/*"的代码行
        int explainCount4;
        //含有注释符"//"的代码行
        int explainCount5;
        String s1;
        Boolean explainValue = false;
        BufferedReader brin = new BufferedReader(new FileReader(filename));
        while ((s1=brin.readLine())!=null) {
            emptyCount = match("\\s",s1);
            emptyCharCount = match("\\S\\W",s1);
            displayCharCount = match("\\w",s1);
            explainCount1 = match("\\/\\/",s1);
            explainCount2 = match("\\/\\*",s1);
            explainCount3 = match("\\*\\/",s1);
            explainCount4 = match("\\S{2,}.*\\/\\*",s1);
            explainCount5 = match("\\S{2,}.*\\/\\/",s1);
            if (explainCount2 != 0) {
                //多行注释开始,判断是注释行还是代码行
                explainValue = true;
                if (explainCount4 != 0) {
                    explainLine--;
                    codeLine++;
                }
            } else if (explainCount3 != 0) {
                //多行注释结束
                explainValue = false;
                explainLine++;
            } else if (explainCount1 != 0) {
                //含单行注释,判断是注释行还是代码行
                if (explainCount5 != 0) {
                    codeLine++;
                } else {
                    explainLine++;
                }
            } else if (!explainValue && displayCharCount == 0 && (emptyCount != 0 || emptyCharCount <= 1)) {
                //空行情况
                emptyLine++;
            } else if (!explainValue) {
                //剩下的是代码行
                codeLine++;
            }
            if (explainValue) {
                //多行注释,超过两行的情况
                explainLine++;
            }
            if (explainCount2 != 0 && explainCount3 != 0) {
                //多行注释只有一行的情况
                explainLine--;
            }
        }
        brin.close();
    }

    public void a(String filename) {
        try {
            read(filename);
            System.out.println("空行数:"+emptyLine);
            System.out.println("代码行数:"+codeLine);
            System.out.println("注释行数:"+explainLine);
        } catch (Exception e) {
            System.out.println("输入路径出错");
        }
    }
}

通过read()函数统计空行数、代码行数、注释行数;多行注释开始时explainValue=true,结束时为false,以此来判断注释行。

 

测试运行

空文件:

个人项目:实现wc.exe(Java)

 

只有一个字符的文件:

个人项目:实现wc.exe(Java)


只有一个词的文件:

个人项目:实现wc.exe(Java)


只有一行的文件:

个人项目:实现wc.exe(Java)


一个典型的源文件:

个人项目:实现wc.exe(Java)

 

代码覆盖率:

个人项目:实现wc.exe(Java)

个人项目:实现wc.exe(Java)

 

PSP

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

30

15

· Estimate

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

30

15

Development

开发

680

1213

· Analysis

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

120

157

· Design Spec

· 生成设计文档

30

75

· Design Review

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

30

60

· Coding Standard

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

30

25

· Design

· 具体设计

80

105

· Coding

· 具体编码

300

566

· Code Review

· 代码复审

60

105

· Test

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

30

120

Reporting

报告

150

145

· Test Report

· 测试报告

60

60

· Size Measurement

· 计算工作量

30

25

· Postmortem & Process Improvement Plan

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

60

60

合计

 

860

1373

项目小结

从PSP表格可以看出,我在学习新技术和具体编码上花的时间最多,这是因为我对java这门语言掌握的不是很到位,有很多常用库函数的用法都没记住,平时做项目练习太少,此后应多加训练敲代码的能力。此次项目运用JUnit的进行单元测试,很方便地得出了代码的一些bug,这比以前的检查bug的方法都要快捷很多,不过本次项目没有采用“测试驱动开发”(TDD)进行,下次项目尝试TDD,以使项目更加便捷地完成。本次项目最大的收获就是学会用科学的开发模式进行开发,了解到需求分析、设计与测试对一个项目的重要性。