GitHub项目地址
https://github.com/Guchencc/WordCounter
组长:
陈佳文:负责词频统计模块与其他模块
组员:
屈佳烨:负责排序模块
苑子尚:负责输出模块
李一凡:负责输入模块
PSP表格
PSP2.1 | PSP阶段 | 预估耗时 (分钟) | 实际耗时 (分钟) |
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 180 | 200 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
· Design | · 具体设计 | 120 | 130 |
· Coding | · 具体编码 | 120 | 150 |
· Code Review | · 代码复审 | 60 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 60 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 10 |
合计 | 750 | 780 |
词频统计模块设计与实现
该模块由三个函数组成:
ArrayList<WordInfo> countFrequency(String filename)
功能:接受文本文件名,读取文本内容,进行词频统计,并将结果存入动态数组中返回。
public static ArrayList<WordInfo> countFrequency(String filename) {
ArrayList<WordInfo> wordInfos=new ArrayList<>();
Pattern pattern=Pattern.compile("[a-zA-Z]+-?[a-zA-Z]*"); //定义单词的正则表达式
String text=Main.readFile(filename); //调用readFile()读取文本内容赋值给text
if (text==null){
return null;
}
Matcher matcher=pattern.matcher(text); //利用之前定义的单词正则表达式匹配text中的单词
String word;
int index;
WordInfo wordInfo;
while(matcher.find()) { //如果匹配到单词则进入循环处理
word=matcher.group().toLowerCase(); //将匹配到的单词赋值给word
if (word.endsWith("-")) //如果匹配到“单词-”情况,则去除符号“-”
word=word.substring(0,word.length()-1);
if ((index=Main.hasWord(wordInfos,word))!=-1) { //如果动态数组wordInfos中已经有该单词,则将频数加一
wordInfos.get(index).setFrequency(wordInfos.get(index).getFrequency()+1);
}else{ //如果动态数组wordInfos中无该单词,则将该单词加入动态数组
wordInfo=new WordInfo(word, 1);
wordInfos.add(wordInfo);
}
}
return wordInfos;
}
原理:
Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过Pattern.complie(String regex)简单工厂方法创建一个正则表达式,轮到Matcher类登场了,Pattern.matcher(CharSequence input)返回一个Matcher对象.
find()对字符串进行匹配,匹配到的字符串可以在任何位置. group()返回匹配到的子字符串
利用Pattern类创建定义单词的正则表达式,在本程序中即
Pattern pattern=Pattern.compile("[a-zA-Z]+-?[a-zA-Z]*");
调用readFile(String filename)读取文本文件内容,将文本赋值给字符串text,再用Pattern类产生Matcher类的实例,即
String text=Main.readFile(filename);
Matcher matcher=pattern.matcher(text);
matcher.find()对字符串进行匹配,若匹配到符合正则表达式的单词则返回true进入循环。
while(matcher.find()) {
......
}
如果匹配的单词类型是“单词-”,则将单词中的“-”符号去掉。
if (word.endsWith("-"))
word=word.substring(0,word.length()-1);
如果当前匹配到的单词,动态数组wordInfos中已经存在,则将该单词频数加一,否则将该单词加入动态数组。
if ((index=Main.hasWord(wordInfos,word))!=-1) {
wordInfos.get(index).setFrequency(wordInfos.get(index).getFrequency()+1);
}else{
wordInfo=new WordInfo(word, 1);
wordInfos.add(wordInfo);
}
int hasWord(ArrayList<WordInfo> wordInfos, String word)
功能:接受动态数组wordInfos和字符串word,判断word是否存在于wordInfos中,若存在则返回其具体位置,否则返回-1。
public static int hasWord(ArrayList<WordInfo> wordInfos, String word) { //判断word是否存在于动态数组wordInfos中,若存在则返回位置,负责返回-1
for (WordInfo wordInfo:wordInfos){
if (wordInfo.getWord().equals(word.trim().toLowerCase()))
return wordInfos.indexOf(wordInfo);
}
return -1;
}
原理:遍历动态数组,寻找word,若存在则返回其index,否则返回-1。
String readFile(String filename)
功能:接受文本文件名,读取该文本内容,并将其以字符串类型返回。
public static String readFile(String filename) { //读取filename文本文件
File file=new File(filename);
StringBuilder sb = new StringBuilder();
try {
FileReader reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
String str;
while ((str = br.readLine()) != null) { //逐行读取文件内容,不读取换行符和末尾的空格
sb.append(str + "\n");
}
br.close();
return sb.toString();
}catch (IOException e){
System.out.println("读取文件失败!");
}
return null;
}
原理:逐行读取文件内容,不读取换行符和末尾的空格。将各行链接起来组成一个字符串。
测试用例的设计
保证设计的测试用例应至少覆盖函数中所有的可执行语句,同时主要针对特殊字符
、数字
、连字符
、大小写字母等
的出现设计测试用例。
单元测试结果
下图为单元测试截图,由图可知,该模块通过了所有测试用例,且时间很短,因此该模块测试质量还是很上乘的。
小组贡献
作为此次小组项目的组长,负责团队开发管理与GitHub项目的管理,并且承担了大部分的代码编辑工作。故给自己的小组贡献分为0.4。
经讨论组员评分情况如下:
组长:
陈佳文 0.40
组员:
屈佳烨:0.35
苑子尚:0.15
李仪凡:0.10