软件工程作业二

一世执手 提交于 2020-03-16 21:03:14

码云项目地址

https://gitee.com/holmec/PersonalProject-Java

PSP表格

PSP2.1 个人开发流程 预估耗费时间(分钟) 实际耗费时间(分钟)
Planning 计划 20 15
· Estimate 明确需求和其他相关因素,估计每个阶段的时间成本 20 15
Development 开发 350 390
· Analysis 需求分析 (包括学习新技术) 40 35
· Design Spec 生成设计文档 20 15
· Design Review 设计复审 20 15
· Coding Standard 代码规范 20 10
· Design 具体设计 30 50
· Coding 具体编码 120 180
· Code Review 代码复审 40 15
· Test 测试(自我测试,修改代码,提交修改) 60 70
Reporting 报告 90 90
· 测试报告 40 40
· 计算工作量 20 20
· 并提出过程改进计划 30 30

解题思路描述

  • 刚看到题目的时候先分析了一下这道题的需求:
  1. 统计文本的字符数
  2. 统计合法的单词数
  3. 统计合法行数
  4. 统计最多的10个单词及其词频
  • 可见要统计首先得先从文件中获取数据,因为考虑到需要统计合法行数,所以我是想按行读取数据,而不是全部读取再来判断换行符之类的。按行读取数据后存储在ArrayList中,以便后续统计时调用,防止多次打开关闭文件造成不必要的异常。
  • 统计文本字符数:遍历列表,转换成字符串再转换成字符数组,遍历字符数组,判断是否是合法字符。
  • 统计合法单词数:遍历列表,转换成字符串,先判断字符串长度是否大于等于4(如果小于4,那肯定不是单词),通过split方法分割每行字符串。遍历分割后的字符串,定义一个标识符来标识是否合法,判断前四个字符串是否是英文字母,若为合法单词,添加到map里。
  • 统计合法行数:遍历列表,转换成字符串,判断字符串长度是否大于0,若大于0则为合法行。
  • 统计最多的10个单词及其词频:利用比较器进行排序。

设计实现过程

一、相关类的设计

  • File类:读取文件数据,写入数据到文件
  • Count类:统计函数的实现
  • Main类:其他类和函数的调用

二、相关函数的设计

1. File类:

  • readfile函数:从文件中按行读取数据并保存到ArrayList中
  • writefile函数:将统计得到的结果写入文件中

2. Count类:

  • CountChars函数:统计文本字符数
  • CountWords函数:统计文本合法单词数
  • CountLine函数:统计文本合法行数
  • WordTop函数:统计最多的10个单词及其词频

代码说明

1. CountChars函数:

public int CountChars(){ //统计文本字符数
        int count=0;
        for(int i=0;i<line.size();i++){ //遍历文本数据
            String str=line.get(i); 
            for(int j=0;j<str.length();j++){ //遍历每行字符串
                char c=str.charAt(j);  //转换成字符
                if(('A'<=c&&c<='Z')||('a'<=c&&c<='z')||c=='\n'||c=='\r'||c=='\t'){
                    count++; //若为合法字符则计数
                }
            }   
        }
        return count;
    }

2. CountWords函数:

public int CountWords(){ //统计文本合法单词数
        int count=0;        
        for(int i=0;i<line.size();i++){ //遍历文本数据
            String str=line.get(i);
            if(str.length()>=4){ //若该行字符串长度大于等于4,则该行可能存在合法单词
                String[] words=str.split("[^a-zA-Z0-9]+"); //通过正则表达式匹配分隔符来分割字符串
                for (String word:words){ //遍历分割后的字符串数组
                    int flag=0; //标识符标识是否为合法单词
                    char[] w=word.toCharArray();
                    for(int j=0;j<4;j++){ //遍历字符串数组的前四个字符
                        if(!(('A'<=w[j]&&w[j]<='Z')||('a'<=w[j]&&w[j]<='z'))){
                            flag=1;
                            break; //若不为英文字母则标识符赋值为1并跳出循环
                        }
                    }
                    if(flag==0){ //若为合法单词                       
                        if(!map.containsKey(word)){ //该单词第一次出现
                            count++; //单词数加1
                            map.put(word, 1); //将键、值添加进map中
                        }
                        else{
                            int num=map.get(word); //该单词已出现过
                            map.put(word, ++num); //单词词频加1
                        }
                    }
                }
            }
        }
        return count;
    }

3. CountLine函数:

public int CountLine(){ //统计文本合法行数
        int count=0;
        for(int i=0;i<line.size();i++){ //遍历文本数据
            String str=line.get(i);
            if(str.length()>0){ //若该行字符串长度大于0,则计数
                count++;
            }
        }
        return count;
    }

4. WordTop函数:

public ArrayList WordTop(int count){ //统计最多的10个单词及其词频
        ArrayList<Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //定义一个list来存放排序后的单词及其词频
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { //重写比较器的排序函数

            @Override
            public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
                if (o1.getValue() == o2.getValue()) {
                    return o1.getKey().compareTo(o2.getKey());
                }
                return o2.getValue() - o1.getValue();
            }

        });
        if(count<=10){ //若单词数不大于10,则直接返回排序后的list
            return list;
        }
        else{ //若单词数大于10,则将第10位以后的单词从list中移除再返回list
            for(int i=list.size();i>=10;i--){
                list.remove(i);
            }
            return list;
        }
    }

单元测试

设计了10个测试点:

  • 测试文件不存在
  • 测试空白行
  • 测试字母大小写是否区别
  • 测试长度小于4的行是否合法
  • 测试空白文件
  • 测试纯中文文件
  • 测试无合法单词
  • 测试合法单词数不大于10
  • 测试合法单词数大于10
  • 测试特殊分隔符

测试代码

package Test;

import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.Test;

import WordCount.Count;
import WordCount.File;

public class CountTest {
    String NoExist="D:\\java练习\\PersonalProject-Java\\201621123033\\NoExist.txt";
    String test1="D:\\java练习\\PersonalProject-Java\\201621123033\\test1.txt";
    String Blank="D:\\java练习\\PersonalProject-Java\\201621123033\\blank.txt";
    String Chinese="D:\\java练习\\PersonalProject-Java\\201621123033\\test2.txt";
    String NoWord="D:\\java练习\\PersonalProject-Java\\201621123033\\noword.txt";
    String more10="D:\\java练习\\PersonalProject-Java\\201621123033\\test3.txt";

    @Test
    public void testreadfile() {
        File file=new File();
        ArrayList<String> line1=new ArrayList<>();
        line1=file.readfile(NoExist);
        boolean test1=(line1.equals(null));
        assertEquals(test1,true);
    }
    
    @Test
    public void testCountChars() {
        File file=new File();
        ArrayList<String> line1=new ArrayList<>();
        ArrayList<String> line2=new ArrayList<>();
        
        line1=file.readfile(Chinese);
        line2=file.readfile(Blank);
        
        Count count1=new Count(line1);
        Count count2=new Count(line2);
        
        int CountChars1=count1.CountChars();
        int CountChars2=count2.CountChars();
        
        assertEquals(CountChars1,0);
        assertEquals(CountChars2,0);
    }

    @Test
    public void testCountWords() {
        File file=new File();
        ArrayList<String> line=new ArrayList<>();
        
        line=file.readfile(test1);
        
        Count count=new Count(line);
        
        int CountWords=count.CountWords();
        int CountLine=count.CountLine();
        
        assertEquals(CountWords,5);
        assertEquals(CountLine,5);
        
    }

    @Test
    public void testCountLine() {
        File file=new File();
        ArrayList<String> line=new ArrayList<>();
        
        line=file.readfile(test1);
        
        Count count=new Count(line);
        
        int CountLine=count.CountLine();
        
        assertEquals(CountLine,5);
    }

    @Test
    public void testWordTop() {
        File file=new File();
        ArrayList<String> line=new ArrayList<>();
        
        line=file.readfile(more10);
        
        Count count=new Count(line);
        int CountWords=count.CountWords();
        
        int WordTop=count.WordTop(CountWords).size();
        
        assertEquals(WordTop,10);
    }

}

结果出现了error

检查代码发现

1.CountWords函数没有考虑到字符数小于4的字符串的情况↓

更改后↓

2.还有WordTop函数

更改后↓

3.还有大小写也有问题

更改后↓

4.增加了文件不存在时的提示信息

再次测试

分支覆盖率

效能分析

下了JProfiler,然而并不会用...再研究一下

心得小结

以前写代码的时候都没有太注意代码规范,命名类、函数、变量等都很随意,一开始写倒还好,写到后面就很容易忘记这个类、函数、变量是什么含义。这次按照要求,命名时规范化了,思路清晰了很多,就算不是一次性写完这个程序,后面再继续写的时候也很容易就能回忆起前面的内容。其实这个程序从实现上来说不难,但是后面测试的时候发现了很多逻辑上错误的地方。以前没有使用过测试工具来进行测试,也没有特别去想一些测试点来测试,这次学习了后觉得测试真的作用还是挺大的。还有之前写代码的时候都是直接大概有个思路就开始写,没有像老师作业要求里说的那样按步骤来写程序,这次按着步骤写程序,感觉确实比较有条理性,甚至感觉写出来的代码都比以前更有条理了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!