zoukankan      html  css  js  c++  java
  • 软工实践第二份作业

    github地址:(https://github.com/LQ77/personal-project)

    一、计划表

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

    二、解题思路

    需求分析

    1. 统计文件有多少个字符,包括非数字字母字符
    2. 统计文件的有效行数。
    3. 统计单词的总数,该单词定义为“至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。”
    4. 统计文件中各单词出现的次数,然后输出频率最高的10个。

    思路

    我选择使用Java完成课题。
    首先,在理解题意后,我先考虑需要几个类、几个对象;而后再考虑类中有几个属性、方法;并且整理好类与类之间的关系。
    初步分析后,我确定了几个需要的类:首先是io包内的File类,new出从外部输入的文本的File类型,再使用util包内的Scanner类,对该文本中的内容进行跟踪、遍历等功能。根据课题需求,我拟定了四个方法去实现四个功能(后续由于统计字符和行数各只需一句话,固不分离,将统计单词数和计算词频并输出的方法分别存在CountWords和CountMaxOfWords这两个类并包装在lib包内)。
    关键的词频统计部分,我联想到了Java中容器Map,其定义了存储键值key和值value的映射对的方法,将单词定义为key,词频定义为value,对统计和遍历将会有极大的帮助,因此我选择使用Map,并构建TreeMap。

    三、设计实现

        String directory = "D:\java\031602123\src";//用于测试的txt文件路径
        File f = new File(directory,args[0]);
        //创建一个File类f,指向test,txt;
        //......
        Scanner input = new Scanner(f)//创建一个Scanner类input指向f;
        //......
        Map<String, Integer> words = new TreeMap<String, Integer>();//创建TreeMap用于词频统计部分;
    
        //利用while循环遍历txt文件内的每一行内容
        while (input.hasNext()) {
    				String line = input.nextLine();
    				countOfCharacters += line.length();//计算当前行的字符个数;
    				countOfLine ++;//行数加一;
    
        /*这是两个关键的方法,首先是在while循环内,每次调用一次countWords方法,对每一行统计符合条件的单词数,记录或更新词频;另一个则是在循环结束后,对Map中的数据进行一次遍历,并从中筛选  出符合条件的10组映射;*/
        countOfWords += CountWords.countWords(line, words);  //CountWords内的static方法countWords
        CountMaxOfWords.Sort(words); //CountMaxOfWords内的static方法Sort
    

    四、程序改进

    在ecilpse中安装JProfiler插件,并进行观察。
    首先先让我们看一下输出的结果:
    运行情况:
    以及内存的分布:

    由于在方法CountWords内对单词是否符合条件的判断需要把String内转换成char[],所以char[]比String多,其次就是int[]与Integer,分别在CountMaxOfWords和TreeMap中使用。同时也能看出,方法CountWords是占用率最多的方法。
    在最开始,统计单词数和统计词频我是分开来做的,但是假设有n个单词,我就需要遍历k次,n<=k<=2n次,于是我决定边统计单词数、同时统计词频;然后是查找需要的10组最大数据,假设有n组数据,我则需要先从map中遍历一遍,时间为O(n),一开始我对整个数据进行排序后再去取,效果很不理想,复杂时间达到了O(nlogn),但是我利用了类堆排序的方法,只需要每次判断10组就行,时间仅为O(nlog10),大大优化了运行状况。

    五、代码说明

    我将展示三个部分的关键代码

    Main类

        //利用Scanner类构建的input对文本内容进行跟踪,hasNext()是一个类似指针的结构,将指针指向当前行的下一行,这样每次循环就能一一对应每一行,并做出分析、处理等;
    		try(Scanner input = new Scanner(f)){
    			int countOfCharacters = 0;
    			int countOfLine = 0;
    			int countOfWords = 0;
    			while (input.hasNext()) {
    				String line = input.nextLine();
    				countOfCharacters += line.length(); //计算当前行的字符个数
    				countOfLine ++;
    				countOfWords += CountWords.countWords(line, words); //CountWords内的static方法countWords
    			}
    

    CountWords类

    流程图:

    部分代码如下

        //将内容全部变为小写,并利用正则表达式"[^\w\d]+",将本行内容分割成每一个只含数字字母字符的小段;
        String mline = line.toLowerCase();
        String[] words = mline.split("[^\w\d]+");
    	int wcount = words.length;//用于统计符合条件的单词数;
    	//进入循环,首先排除掉长度小于4的字符段,其次对其进行判断是否符合条件、而后统计进Map容器内;
    	for(int i=0; i<words.length; i++){
    		if(words[i].length() < 4)
    			wcount--;
    		if(words[i].length() >= 4){
    			char[] word = words[i].toCharArray();
    			int mcount = 0;
    			for(int j=0; j<4; j++){
    				if((word[j]>='0') && (word[j]<='9')){
    					mcount++;
    				}
    			}
    			//只有经历过上方循环后mcount=0才表示该字符段符合单词条件
    			//在Map内记录或更新单词——词频映射对
    			if(mcount == 0){
    				if(m.containsKey(words[i])){
    					int v = (Integer)m.get(words[i]);
    					v++;
    					m.put(words[i],v);
    				}
    				else{
    					m.put(words[i],1);
    				}
    			}
    			else{
    				wcount--;
    			}
    		}
    	}
    

    CountMaxOfWords类

    流程图:

    部分代码如下

        int s = m.size();
        int[] a = new int[10];
    	a[0] = 0;		
    	int type = 0;
    	String[] wd = new String[10];
        /*利用Map.Entry完成对Map内的数据进行以key为基准的遍历,type为一个标准,界定于10内外;
        当数组a存放了10组数据后,调用选择排序方法bbsort,将最小数据放入a[0],之后对于新的数据比较value与a[0],大于则插入,并调用bbsort;*/
        for(Map.Entry<String, Integer> entry : m.entrySet()){
    		String k = entry.getKey();
    		int v = entry.getValue();
    		if((type < 10)&&(type != s)){
    			a[type] = v;
    			wd[type] = k;
    			type++;
    		}
    		if((type == s)||(type == 10)){
    			if(v >= a[0]){
    				a[0] = v;
    				wd[0] = k;
    			}
    			bbsort(a,wd,s);
    		}
    	}
    

    六、异常处理

    各个方法的覆盖率:

    做了部分的测试:

    1. 传入空文件,输出结果全为0,符合预期;
    2. 不传入文件,输出:Please input any txt,符合预期;
    3. 输入错误或不存在的文件名,输出:The txt does not exist,符合预期;
    4. 输入单词数小于10的文件,文件内容和输出结果如下,符合预期;

    七、总结

    在这次课题中,我遇到得最大问题就是最新技术的学习和运用,利用这次暑假,虽然对Java有了一定程度的了解,但是JProfiler以及各种插件的使用、程序的改进、异常的处理等等,都很少去做,这次课题实打实的让我完成了一次关于与Java有关的小项目,体会到了“纸上得来终觉浅”。但是通过这次课题,自己的自学能力和代码的查错、改进能力有了不小的进步。虽然求学、求实之路不易,但是自己也在实践之中受益良多,为之后奠定了基础,也让我抱有期待。
    
  • 相关阅读:
    进程间的通信如何实现?
    试解释操作系统原理中的作业、进程、线程、管程各自的定义。
    字符数组和strcpy
    字符串转化成整数
    整数字符串转化
    海量数据/日志检索问题
    哈夫曼编码问题
    Trie树,又称单词查找树、字典
    初识面向对象
    序列化 json pickle shelve configparser
  • 原文地址:https://www.cnblogs.com/lq777/p/9623037.html
Copyright © 2011-2022 走看看