作业要求:《实验二 软件工程个人项目》
本项目Github的仓库主页
1. 需求分析
本次实验要求使用JAVA编程语言,独立完成一个英文文本词频统计的软件开发。
该程序要求实现:
- 要求能够读入任意英文文本文件,该文件中英文词数大于等于一个;
- 要能够容纳英文原版《哈利波特》10万词以上的文章;
- 要有指定单词的词频统计功能,即用户可输入从该文本中想要查找词频的一个或任意多个英文单词,运行程序的统计功能可显示对应单词在文本中出现的次数和柱状图;
- 高频单词统计功能,即用户共键盘输入高频词输出个数k,运行程序统计功能,可按文本中词频数降序显示前k个单词的词频及单词;
- 统计该文本所有单词数量及词频数,并能将单词及词频数按字典顺序输出到文件result.txt
2. 功能设计
根据需求分析中提到的内容,本程序中设计以下几个功能:
- 统计文件中所有单词及其词频数,且输出到指定文件;
- 统计指定单词在文件中出现的次数;
- 输出前K个高频词的个数及其柱状图;
程序流程图:
3. 设计实现
本程序包含四个类,包括一个主函数Main.java和三个功能类,分别是Count1.java,Count1.java,Count1.java主程序主要是用来控制程序的整个流程,用户输入不同的功能选项,来调用这三个功能。三个类的功能分别是:
- Count1.java:该类是用来实现对指定文件中所有单词及其词频进行统计功能的,统计完成后并将统计结果输出到指定文件中。
- Count2.java:该类是用来实现用户从键盘输入任意一个单词,查询并统计该单词在文件中出现的次数功能的,若查询的该单词不存在则显示提示信息。
- Count3.java:该类是用来实现统计用户指定任意个数的高频词频及其柱状图功能的。
4. 测试运行
-
本程序主界面显示如下:
-
功能选项1测试:
选择选项1,显示文件中所有单词个数及其词频统计结果,并将结果输出到指定文件中。
-
功能选项2测试:
用户任意输入想要在文件中查询的单词,查询并显示结果,若该文件中不存在该单词则提示错误信息。
-
功能选项3测试:
用户输入想要查询统计的任意个数K个高频单词,查询成功则显示单词词频及其柱状图
若输入查询个数为不在有效范围内(负数或者大于文中单词类型个数),则提示相应的错误信息。
-
功能选项4:
退出程序,不在输出菜单。
5. 代码片段
- 读取指定文件
String filecontent = "";//用来存放读取到的文件内容信息
//用键值对分别存储单词和它出现的次数
Map<String,Integer> map = new HashMap<String, Integer>();
try {
//读取指定文件DATA.txt,这里使用的是相对路径
// FileInputStream fis = new FileInputStream("J:\计算机科学与技术\02 第二学期\2 软件工程(代祖华)\实验二 词频统计\DATA.txt");
FileInputStream fis = new FileInputStream("src\DATA.txt");
//创建BufferedReader的缓冲流,一次性读取很多数据,然后按要求分次交给上层调用者
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String str = "";
try {
//一行一行读取
while ((str = br.readLine()) != null){
filecontent = filecontent + str;
}
} catch (IOException e) {
e.printStackTrace();
}
}catch (FileNotFoundException e){
e.printStackTrace();
}
- 分割字符串并统计键值对
//arrspilt数组用来统计单词出现的次数
String [] arrspilt = filecontent.split("[^a-zA-Z]+");
for (int i=0;i<arrspilt.length;i++){
//通过键名来获取键值
if (map.get(arrspilt[i]) == null){
//统计键值对
map.put(arrspilt[i],1);
}else {
int count = map.get(arrspilt[i]);
map.put(arrspilt[i],++count);
}
}
- 对词频统计结果进行排序
//利用TreeMap实现Comparator接口
Comparator<Map.Entry<String, Integer>> valcom = new Comparator<Map.Entry<String,Integer>>() {
//对词频统计结果进行排序
public int compare(Map.Entry<String, Integer> rst1,Map.Entry<String, Integer> rst2) {
//降序排序
int sortrst=rst2.getValue()-rst1.getValue();
return sortrst;
}
};
List<Map.Entry<String, Integer>> ordlist = new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
// 用sort函数来排序
Collections.sort(ordlist,valcom);
- 将词频统计结果输出到指定文件results.txt中
//将词频统计结果输出到指定文件results.txt中
try {
FileWriter fw= new FileWriter("src\results.txt");
BufferedWriter bw= new BufferedWriter(fw);
for (Map.Entry<String, Integer> entry : ordlist) {
bw.write(entry.getKey() + ":" + entry.getValue());
bw.newLine();
}
bw.flush();//刷新
bw.close();//关闭
System.out.println("词频统计结果已输出到result.txt文件!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 统计任意单词在文件中出现次数
List<Map.Entry<String, Integer>> ordlist = new ArrayList<Map.Entry<String,Integer>>(map.entrySet());
// 用sort函数来排序
Collections.sort(ordlist,valcom);
Scanner sc = new Scanner(System.in);
System.out.println("请输入想要统计出现在文章中的单词:");
String w = sc.next();
for (Map.Entry<String, Integer> entry : ordlist) {
if(entry.getKey().equals(w)){//找到文件中与键盘录入单词相同的单词
word=entry.getKey();
count2=entry.getValue();
flag=1;
break;
}else{
flag=0;
}
}
if(flag==1){
System.out.println("该单词"+word+"出现的个数为:"+count2);
}else{
System.out.println("对不起文章中没有出现该单词!");
}
- 显示K个高频词及其柱状图
System.out.println("注:该文件中共有"+ordlist.size()+"个不同的单词!");
Scanner sc = new Scanner(System.in);
System.out.println("请输入要查看的高频词频的个数:");
int k = sc.nextInt();
//String k=sc.next();
//System.out.println(list.size());
if(k>0&&k<=ordlist.size())//判断输入值是否超出范围
{
System.out.println("----------词频最高的前"+k+"个单词及其柱状图-----------");
for (Map.Entry<String, Integer> entry : ordlist) {
//System.out.println("单词 "+entry.getKey() + " 出现 " + entry.getValue()+" 次 ");
System.out.printf("单词 "+"%-8s" + "出现 " +"%-3d"+" 次 |",entry.getKey(),entry.getValue());
//用符号个数来模拟柱状图
for(int i=entry.getValue();i>0;i--)
{
System.out.print("⬛");
}
System.out.println();
//System.out.println(entry.getKey() + ":" + entry.getValue());
if(--k==0)
break;//如果k==0则不再输出后面的词频
}
System.out.println("------------------------------------------------");
}else{
System.out.println("输入有误!请重新输入!");
}
6. 总结:设计的程序如何实现软件设计模块化的原则
模块化程序设计是指在进行程序设计时将一个大程序按照功能划分为若干小程序模块,每个小程序模块完成一个确定的功能,并在这些模块之间建立必要的联系,通过模块的互相协作完成整个功能的程序设计方法。
在本程序中,需要实现全文单词个数及其词频统计,用户输入任意单词词频统计和任意个数高频词频统计及柱状图显示等多个功能,虽然这个程序只是一个小程序,但却也包含多个功能,如果我们将这若干个功能放在一个文件中,就会显得很臃肿,凌乱和复杂,在调试和维护时也很麻烦,最好的方式就是一个功能一个文件,将功能分散开来,用主程序,子程序等框架把软件的主要结构和流程描述出来,并定义和调试各部分模块,再通过主函数来实现对其他功能的调用,以功能块为单位进行程序设计,这样降低录了程序复杂度,使程序设计,调试等操作简单化。
7. PSP个人软件过程
PSP2.1 | 任务内容 | 计划共完成需要的时间(min) | 实际完成需要的时间(min) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
•Estimate | • 估计这个任务需要多少时间,并规划大致工作步骤 | 10 | 10 |
Development | 开发 | 255 | 295 |
••Analysis | 需求分析 (包括学习新技术) | 10 | 15 |
•Design Spec | • 生成设计文档 | 5 | 7 |
•Design Review | • 设计复审 (和同事审核设计文档) | 5 | 7 |
•Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 5 | 6 |
•Design | 具体设计 | 10 | 10 |
•Coding | 具体编码 | 200 | 220 |
•Code Review | • 代码复审 | 5 | 10 |
•Test | • 测试(自我测试,修改代码,提交修改) | 15 | 20 |
Reporting | 报告 | 13 | 15 |
••Test Report | • 测试报告 | 4 | 3 |
•Size Measurement | 计算工作量 | 4 | 4 |
•Postmortem & Process Improvement Plan | • 事后总结 ,并提出过程改进计划 | 5 | 8 |
从本次这个词频统计程序的开发过程来看,我在做这项工作时是用来不少的时间,尤其是在开发过程中的编码设计阶段,由于很长时间没有再写代码,关于java好多知识都忘得差不多了,所以在编码时需要耗费时间来查资料,来纠错改错,但也基本完成了本次任务,看来日后要多加强代码的编写练习。