个人作业2——WordCount

个人作业2——WordCount

标签(空格分隔): 软件工程


Task1:Fork仓库的码云地址

码云地址:https://gitee.com/JiuSong/PersonalProject-Java

Task2:PSP表格

PSP2.1个人开发流程预估耗费时间(分钟)实际耗费时间(分钟)
Planning计划1020
· Estimate明确需求和其他相关因素,估计每个阶段的时间成本2020
Development开发240300
· Analysis需求分析 (包括学习新技术)2030
· Design Spec生成设计文档2025
· Design Review设计复审2020
· Coding Standard代码规范1010
· Design具体设计2026
· Coding具体编码120150
· Code Review代码复审2025
· Test测试(自我测试,修改代码,提交修改)6070
Reporting报告80110
·测试报告2025
·计算工作量1015
·并提出过程改进计划2015

Task3:解题思路描述

题目的需求分析:

  • 统计文件的字符数:
    • 只需要统计Ascii码,汉字不需考虑
    • 空格,水平制表符,换行符,均算字符
    • 统计文件的单词总数,单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写
    • 英文字母: A-Z,a-z
    • 字母数字符号:A-Z, a-z,0-9
    • 分割符:空格,非字母数字符号
    • 例:file123是一个单词, 123file不是一个单词。file,File和FILE是同一个单词
  • 统计文件的有效行数:任何包含非空白字符的行,都需要统计。
  • 统计文件中各单词的出现次数,最终只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。
  • 按照字典序输出到文件result.txt:例如,windows95,windows98和windows2000同时出现时,则先输出windows2000
    • 输出的单词统一为小写格式
  • 输出的格式为
    characters: number
    words: number
    lines: number
    <word1>: number
    <word2>: number
    ...

思路分析:

刚拿到题目时,就先分析了一下需求,经过网上查找资料,经过如下分析过程:

  •   问题首先需要解决的是如何读文件?如何写文件?整个的类的封装要怎么构造。
  •   统计文件字符数时,又有一个重点就是不区分大小写并且是输出单词统一为小写格式,所以我想到的是先对文件进行处理,读出来的整个内容赋予给一个字符串,在进行单词数量统计之前将字符串统一转化为小写格式string.toLowerCase()
  •   题目要求讲的单词至少4个英文字母开头,所以FILE1234可以作为单词,但123FILE这就不能算作单词。又因为要统计单词的频率,所以可以用Map来写,单词作为Key,频数作为Value值。
  •   统计总的字符数,可以对全部内容进行读取,用换行('\n')、回车('\r')来进行数组的切分,这样每一组都是原来的一行,然后对每一个数组元素去除空格后进行比较,如果空则原来是空内容的一行。

  最后综合以上分析可以得到流程图如下:
1099326-20180917210307749-1109306111.png

Task4:设计实现过程

一、相关类的设计

  针对这个要求,得到一个主函数,两个类,其中:

  • Word类:进行字符数量、行数、单词数、词频的统计。
  • OperateFile类:进行文件内容的读取,以及处理结果的写入。
  • Main类:结合实际情况,创建以上两种对象,进行方法的调用,实现题目要求。

二、相关函数的设计

Word:

  1. wd.getcharNum(); //字符数统计
  2. wd.getwordNum();//单词数统计
  3. wd.getlineNum();//有效行统计
  4. wd.getwordFreq();//单词频率统计

OperateFile:

  1. fd.FileToString(file);//读出文件内容变成字符串
  2. fd.WriteToFile(w);//结果写入指定文件

Task5:代码说明

1. getcharNum()

//统计文件的字符数:只需要统计Ascii码,汉字不需考虑空格,水平制表符,换行符,均算字符
    public int getcharNum()
    {
        char ch;
        for(int i = 0;i<str.length();i++)
        {
            ch = str.charAt(i);//String str = "abc";char ch = str.charAt(0);char ch2 = str.charAt(1);这时候ch是a,ch2是b;
            if(ch>=32 && ch<=126 || ch == '\n' || ch == '\r' || ch == '\t') //换行('\n')、回车('\r')、水平制表符('\t')、垂直制表符('\v')
            {
                charNum++;
            }
        }
        return charNum;
    }

2. getwordNum()

//统计文件的单词总数,单词:以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。

节选:
for(int i = 0;i<everyword.length;i++)
        {
            
            if(everyword[i].length()<4)
            {
                continue;
            }
            else //符合长度的单词
            {
                int flag=0;
                char[] ch = everyword[i].toCharArray();//每一个everyword数组内容拆分成字符数组
                for(int j = 0;j<4;j++)
                {
                    if(!(ch[j]>= 'A' && ch[j]<= 'Z' || ch[j]>= 'a' && ch[j]<= 'z'))
                    {
                        flag=1;
                    }
                }
                if(flag ==0 )
                {
                    wordNum++;
                }
            }
        }

3. getlineNum()

//统计文件的有效行数:任何包含非空白字符的行,都需要统计。
        public int getlineNum()
        {
            String[] line = str.split("\n|\r");//换行('\n')、回车('\r')
            for(int i=0; i<line.length;i++)
            {
                if(line[i].trim().isEmpty())
                {
                    continue;
                }
                else
                {
                    lineNum++;
                }
            }
            return lineNum;
        }

4. getwordFreq()

//单词频率统计
public List<Map.Entry<String, Integer>> getwordFreq()
    {
        wordFreq = new HashMap<String,Integer>();
        
        String s = str;
        
        s = s.replace('\n',' ');
        s = s.replace('\r',' ');
        s = s.replace('\t',' ');
        
        String[] everyword = s.split(" ");//用空格作为分割
        for(int i = 0;i<everyword.length;i++)
        {
            
            if(everyword[i].length()<4)
            {
                continue;
            }
            else 
            {
                int flag=0;
                char[] ch = everyword[i].toCharArray();
                for(int j = 0;j<4;j++)
                {
                    if(!(ch[j]>= 'A' && ch[j]<= 'Z' || ch[j]>= 'a' && ch[j]<= 'z'))//开头四位不全为字母
                    {
                        flag=1;
                    }
                }
                if(flag ==0 )//符合一个单词的标准
                {
                    String key = everyword[i].trim().toLowerCase();//统一转换成小写
                    if (wordFreq.containsKey(key)){
                        int n=Integer.parseInt(wordFreq.get(key).toString())+1;
                        wordFreq.put(key,n);
                    }else {
                        wordFreq.put(key,1);
                    }
                }
            }
        }
        
        
        List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(wordFreq.entrySet());  
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {

            @Override//降序排序  
            public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
                // TODO Auto-generated method stub
                if(o1.getValue() == o2.getValue())
                {
                    return o1.getKey().compareTo(o2.getKey());
                }
                return o2.getValue().compareTo(o1.getValue()); 
            }  
        });  
        return list;
       /* for (Entry<String, Integer> mapping : list) {  //输出
            System.out.println(mapping.getKey() + ":" + mapping.getValue());  
        }  */
    }
    

  该函数用于单词词频的统计,按照要求首先判断词是否为单词,然后实验map进行储存,由于map的key不可重复,则每次写入时需要判断map之前是否存在过该数据,若没有存在过,则该value值为1,否则,在其value值上再加1.
统计后,将map改为list存储,再用Collections.sort进行排序,通过重写comparator来实现要求的排序。

Task6:单元测试

  根据以上函数,设置了一些测试点,尽可能罗列出各种情况:

  • 字符数为空
  • 完全的中文文件
  • 完全的英文文件
  • 完全的英文文件,大小写混合
  • 完全的英文文件,无合法单词

对应设置的文件为:

  • test1:完全的空文件
  • test2:纯中文文件,不含有英文
  • test3:纯英文文件,完全小写
  • test4:纯英文文件,大小写混合
  • test5:英文、数字、特殊符号混合

测试代码:

package WordCount;

import static org.junit.Assert.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import org.junit.Before;
import org.junit.Test;

public class WordTest {

    
    
    //String file1 = "text1.txt";
    //String file2 = "text2.txt";
    //String file3 = "text3.txt";
    //String file4 = "text4.txt";
/*  Scanner sc =new Scanner(System.in); 
    String file1 = sc.next();
    String file2 = sc.next();
    String file3 = sc.next();
    String file4 = sc.next();
    */
    
    
    @Before
    public void setUp() throws Exception {
    }
    
    
    
    //统计字符数量测试
    @Test
    public void testGetcharNum() throws IOException {
        OperateFile fd = new OperateFile();
        
    //  System.out.print(file1);
        String str1 = fd.FileToString("test1.txt");//读出文件内容变成字符串
        String str2 = fd.FileToString("test2.txt");
        String str3 = fd.FileToString("test3.txt");
        String str4 = fd.FileToString("test4.txt");
        
        Word wd1 = new Word(str1);
        Word wd2 = new Word(str2);
        Word wd3 = new Word(str3);
        Word wd4 = new Word(str4);
        
        int p1 = wd1.getcharNum();//调用
        int p2 = wd2.getcharNum();
        int p3 = wd3.getcharNum();
        int p4 = wd4.getcharNum();
        
        assertEquals(0, p1);//测试与结果匹配
        assertEquals(37, p2);
        assertEquals(86, p3);
        assertEquals(272, p4);
    }

    @Test
    public void testGetwordNum() throws IOException {//统计单词数量测试
        OperateFile fd = new OperateFile();
        
        String str1 = fd.FileToString("test1.txt");//读出文件内容变成字符串
        String str2 = fd.FileToString("test2.txt");
        String str3 = fd.FileToString("test3.txt");
        String str4 = fd.FileToString("test4.txt");
        
        Word wd1 = new Word(str1);
        Word wd2 = new Word(str2);
        Word wd3 = new Word(str3);
        Word wd4 = new Word(str4);
        
        int p1 = wd1.getwordNum();//调用
        int p2 = wd2.getwordNum();
        int p3 = wd3.getwordNum();
        int p4 = wd4.getwordNum();
        
        assertEquals(0, p1);//测试与结果匹配
        assertEquals(0, p2);
        assertEquals(10, p3);
        assertEquals(35, p4);
    }

    @Test
    public void testGetlineNum() throws IOException {//统计有效行数测试
        OperateFile fd = new OperateFile();
                
        String str1 = fd.FileToString("test1.txt");//读出文件内容变成字符串
        String str2 = fd.FileToString("test2.txt");
        String str3 = fd.FileToString("test3.txt");
        String str4 = fd.FileToString("test4.txt");

        
        Word wd1 = new Word(str1);
        Word wd2 = new Word(str2);
        Word wd3 = new Word(str3);
        Word wd4 = new Word(str4);
        
        int p1 = wd1.getlineNum();//调用
        int p2 = wd2.getlineNum();
        int p3 = wd3.getlineNum();
        int p4 = wd4.getlineNum();
        
        assertEquals(0, p1);//测试与结果匹配
        assertEquals(6, p2);
        assertEquals(4, p3);
        assertEquals(12, p4);
    }

    @Test
    public void testGetwordFreq() throws IOException {//统计词频测试
        OperateFile fd = new OperateFile();
        
        String str1 = fd.FileToString("test1.txt");//读出文件内容变成字符串
        String str2 = fd.FileToString("test2.txt");
        String str5 = fd.FileToString("test5.txt");
    
        
        Word wd1 = new Word(str1);
        Word wd2 = new Word(str2);
        Word wd5 = new Word(str5);

        

        List<Map.Entry<String, Integer>> wordFreq1 = wd1.getwordFreq();
        List<Map.Entry<String, Integer>> wordFreq2 = wd2.getwordFreq();
        List<Map.Entry<String, Integer>> wordFreq5 = wd5.getwordFreq();
        
        Map<String,Integer> w1 = new HashMap<String,Integer>();
        Map<String,Integer> w2 = new HashMap<String,Integer>();
        Map<String,Integer> w3 = new HashMap<String,Integer>();
    //  Map<String,Integer> w4 = new HashMap<String,Integer>();
        w3.put("jiusong", 4);
        w3.put("test111", 1);
    
        List<Map.Entry<String, Integer>> t1 = new ArrayList<Map.Entry<String, Integer>>(w1.entrySet());  
        List<Map.Entry<String, Integer>> t2 = new ArrayList<Map.Entry<String, Integer>>(w2.entrySet());
        List<Map.Entry<String, Integer>> t3 = new ArrayList<Map.Entry<String, Integer>>(w3.entrySet());
        
        assertEquals(t1, wordFreq1);
        assertEquals(t2, wordFreq2);
        assertEquals(t3, wordFreq5);

        
        /*
        jiusong=4
                test111=1*/
    }

}


测试结果:

1099326-20180923015057271-1428928600.png

分值覆盖率截图:

1099326-20180923142436432-854637155.png

代码内的覆盖率:

  在右侧工具栏中点击”Coverage”图标,打开Coverage的视图后,代码字体背景变红/变绿/变黄原因,经过百度后,有关覆盖测试的颜色含义如下:

Source lines containing executable code get the following color code:
    1.green for fully covered lines,
    2.yellow for partly covered lines (some instructions or branches missed) and
    3.red for lines that have not been executed at all.

即:

  • 绿色为完全覆盖的线条,
  • 黄色部分覆盖的线条(一些指示或分支遗漏)
  • 红色表示尚未执行的行

测试的代码中,在word处理类里面这两部分吗没有完全被覆盖:

1099326-20180923144127854-1574692731.png

1099326-20180923144129827-454162568.png

Task7:效能分析

分析图,由Jprofiler生成:

运行结果:

1099326-20180923142459303-1852119330.png

1099326-20180923142506740-2131143143.png

1099326-20180923142513674-1048567728.png

分析:

  根据以上生成的图,可知道char[]、String用得比较多。在统计文件的单词总数中,有用到char[],嵌套在一个大的for循环中,可能是因为循环的缘故导致调用的频率比较高。因为对一个文件运行时,相当于把整个文件中的每个字符都变成了char[]中的一员,数量比较庞大。

Task8:心得体会

  在这次试验中,我发现自己学到了很多编写代码以外其他的很多内容,一个项目的核心不仅仅是代码,它的性能以及准确性也十分重要。之前我以为做软件就是要编写代码,现在我发现做单元测试进行分析是一件十分有意思的事情,尤其是eclemma得到的覆盖率,每个调用的多少频率如何,这可以帮助我们找到自己编写的代码的思维逻辑上的错误,更加快速准确的找到问题所在。
  后面的jprofiler也十分的厉害,但是安装消耗了很久的时间,安装出了问题的解决也用了很久,可能是网上关于出错的教程太少,很多问题很难解决。还有就是这个软件只能做到很表面的一个小测试,有些测试给出的结果图有些看不懂,分析起来也很费劲。所以这边的截图只能粗略浅显的大概进行了一波分析
  总的来讲,这一次学到了很多,让我的观点也发生了一些改变,我觉得软件不再是单纯地写代码枯燥的事情,还可以进行分析进行测试,加大了我对这个事情的一个兴趣度。

转载于:https://www.cnblogs.com/qin-yu/p/9664529.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值