班级:软件工程1916|W(福州大学)
做业:结对第二次—文献摘要热词统计及进阶需求
结对学号:221600315 黎焕明 221600319 李彦文
GitHub基础需求项目地址:基础需求
GitHub进阶需求项目地址:进阶需求
做业目标: 实现一个可以对文本文件中的单词的词频进行统计的控制台程序。在基本需求实现的基础上,编码实现顶会热词统计器。
具体分工:两我的一块儿负责分析需求,而后主要分析需求和文档编写主要是221600319,221600315主要负责部分代码的实现。git
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务须要多少时间 | 660 | 1010 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 180 | 60 |
• Design Spec | • 生成设计文档 | 60 | 180 |
• Design Review | • 设计复审 | 60 | 120 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 无 | 无 |
• Design | • 具体设计 | 120 | 180 |
• Coding | • 具体编码 | 240 | 300 |
• Code Review | • 代码复审 | 0 | 200 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | ||
• Test Report | • 测试报告 | 无 | 无 |
• Size Measurement | • 计算工做量 | 无 | 无 |
• Postmortem & Process Improvement Plan | • 过后总结并提出过程改进计划 | 60 | 60 |
合计 | 660 | 1010 |
1. 基本需求:拿到题目后,看到基本需求时感受并不难,由于以前写过相似的程序。要求知足三个功能,第一个是字符统计,第二个是单词统计,第三个是词频统计。基本想法是将这三个功能分别写到三个函数中,而后经过主函数调用返回相应的结果而且打印输出。 2. 进阶需求:是在基本需求的基础上添加了参数判断要求,新增的功能是加入词组统计、单词或词组加入权重来计算最终的频数。参数判断单独封装,程序执行开始前就要将全部参数所有处理,方便以后使用,因此将参数判断结果利用类属性记录。首先是加入权重这个问题,咱们的想法是直接在词频统计函数中加入判断功能,若是开启权重功能,那么属于TITLE的词出现一次词频加10,其余状况都同样。词组统计功能,将这个功能单独分装成一个函数,利用正则来匹配词组。 3. 资料查找:通过初步分析以后,上网看了一些介绍单词统计,词组统计的博客。大体了解了一下他们的思路,基本有两种,一种是循环读取判断,再一种就是正则表达式匹配。咱们更倾向于后一种,这种方法比较直观,体现再代码上就是比较简洁。
一共用两个类,一个类是主程序入口Main,另一个是文本处理类Lib。Main类调用Lib类的静态函数countChars、countWords、countLines分别统计字符数单词数和行数,而后直接输出,总体结构较为简单。
类图:
github
在基本功能完成后,进行测试时发现,若是测试文件比较大,程序读取文件很是缓慢。经过debug发现是由于在读取文件时使用了节点流,当文件读取采用处理流方式时文件读取会快不少。 由于刚开始写时需求分析时间不多就开始写代码,致使咱们直接用了比较“土”的方法来实现,在进阶需求写爬虫才想起来用正则表达式实现能够简单地很是多。同时由于List<StringBuilder>的deleteChar方法效率很是低,咱们直接从新分配了内存,这部分处理时间复杂度缩短了很是多。
基础需求实现比较简单,可是countWords有必定的难度,刚开始并无想到用正则,而是直接遍历。该方法接收StringBuilder的List并返回一个哈希表,该方法遍历List的每一行,而后遍历该行,若是找到单词就检查哈希表是否存在,若是哈希表存在该单词就取出哈希表里面的值+1并put进哈希表,若是哈希表不存在该单词,则将该单词直接put进哈希表,key为单词,value为1。最后对哈希表进行按值排序并返回哈希表。
/** * 统计单词数 * @param stringList * @return */ public static Map<String,Integer> countWords(List<StringBuilder> stringList){ Map<String,Integer> hashTable=new LinkedHashMap<>(); /* 计数器 */ int count; /* 是否为单词的标志*/ boolean flag; /* 统计单词数,若是存在单词就放进哈希表 */ for(StringBuilder line:stringList){ count=0; flag=true; for(int i=0;i<line.length();++i){ if(line.charAt(i)<='z'&&line.charAt(i)>='a'){ ++count; }else if(line.charAt(i)<='9'&&line.charAt(i)>='0'){ if(count<4){ flag=false; } ++count; }else if(count>3&&flag){ String key=line.substring(i-count,i); /* 将单词存入哈希表 */ hashTable.merge(key, 1, (a, b) -> a + b); count=0; }else{ count=0; flag=true; } } } /* 对哈希表进行排序 */ List<Map.Entry<String, Integer>> keyList = new LinkedList<Map.Entry<String, Integer>>(hashTable.entrySet()); keyList.sort( new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return Integer.compare(o2.getValue().compareTo(o1.getValue()), 0); } }); hashTable.clear(); for(Map.Entry<String, Integer> entry:keyList){ hashTable.put(entry.getKey(),entry.getValue()); } return hashTable; }
单元测试:
类图
正则表达式
/** * 统计词组词频 * @param stringList * @return */ static Map<String,Integer> countWords(int delete, int m, List<StringBuilder> stringList){ Map<String,Integer> hashTable=new LinkedHashMap<>(); // 建立 Pattern 对象 String patternString = "^[a-z]{4}[a-z0-9]*"; Pattern pattern = Pattern.compile(patternString); boolean flag; for(StringBuilder line:stringList){ String volatileString= ""; if(line.length()>delete) { volatileString = line.substring(delete); } String notAlphaandNumber="[^a-z0-9]+"; String [] strings=volatileString.split(notAlphaandNumber); for(int i=0;i<strings.length-m+1;i++){ //是否全为单词 flag=true; for(int j=i;j<i+m&&j<strings.length;++j){ Matcher matcher = pattern.matcher(strings[j]); if(!matcher.find()){ flag=false; break; } } //若是全为单词 if(flag){ StringBuilder regEx= new StringBuilder(); for(int k=0;k<m;++k){ regEx.append(strings[i + k]); if(k!=m-1)regEx.append(notAlphaandNumber); } Pattern compile = Pattern.compile(regEx.toString()); Matcher matcher=compile.matcher(volatileString); if(matcher.find()){ hashTable.merge(matcher.group(), 1, (a, b) -> a + b); } } int beginIndex=volatileString.indexOf(strings[i]); // int endIndex=volatileString.indexOf(strings[i+m-1])+strings[i+m-1].length(); int endIndex=volatileString.indexOf(strings[i])+strings[i].length(); // volatileString=volatileString.substring(0,beginIndex)+volatileString.substring(endIndex); volatileString=volatileString.substring(0,beginIndex)+volatileString.substring(endIndex); } } return hashTable; }
改进的思路:
刚开始也是使用基本需求那样一个个比对,可是由于写爬虫的时候忽然想起来还有正则表达式,因而直接使用正则表达式进行匹配,而后使用Pattern.compile能够更加快速的进行正则匹配,同时由于String是Immutable的,因此进阶需求和基础需求同样都是使用StringBuilder进行字符串处理。数组
爬虫是本身使用Python实现的,直接用正则匹配全部div为ptitle的节点并获取连接,而后对连接的网页内容进行正则匹配将结果输出到文件中。使用方法:Python main.pyapp
# coding=utf-8 import re import requests respose=requests.get('http://openaccess.thecvf.com/CVPR2018.py') text=respose.text urls=re.findall(r'class="ptitle".*?href="(.*?)"',text,re.S) output=open("result.txt","w",encoding='utf-8') j=0 print(respose.encoding) for i in urls: url='http://openaccess.thecvf.com/'+i respose = requests.get(url) text = respose.text paper_title = re.findall('id="papertitle">\n(.*?)<',text, re.S) abstract = re.findall('id="abstract" >\n(.*?)<', text, re.S) output.write(str(j)+"\n") output.write("Title: ") output.write(paper_title[0]) output.write("\n") output.write("Abstract: ") output.write(abstract[0]) output.write('\n\n\n') j+=1
重头开始需求分析,而后重构代码。
经过分离不一样的模块和功能下降总体的耦合度。
下次记得及时和多沟通。
221600315代码能力较强,在需求分析完成后很快就将部分基本代码写了出来,确实很厉害。讨论以后肯定要用Java实现,我本身由于Java很久没看了,因此要从新复习一下,怕时间来不及,而后就让队友先写。在写的过程当中共同讨论遇到的问题,以及需求方面的一些研究。此次做业队友花费的时间比较多,让我有了时间复习Java,仍是很是感谢他的。