软件工程实践之词频统计
GitHub仓库地址: https://github.com/softwarehsc/java1
PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
合计 | 540 | ||
· Test Repor | · 测试报告 | 60 | 40 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 240 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 60 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 | 30 | 10 |
· Design | · 具体设计 | 60 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 15 |
· Coding | · 具体编码 | 120 | 380 |
· Code Review | · 代码复审 | 60 | 30 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
Reporting | 报告 | 80 | 60 |
Planning | 计划 | 60 | 60 |
Development | 开发 | 480 | 795 |
需求分析
-
统计文件的字符数:
- 只需要统计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
实现过程
在eclipse建立文件main.java包含文件的读入和主方法,调用lib.java中Print类的求characters、words、lines等的方法实现需求。
在WordCount类中首先通利用BufferedReader类通过从字符输入流中读取文本。将所有字母单词转换为小写形式,再次读取文档,通过正则表达式
过滤掉标点符号分离出单词存储在lists列表中(List<String> lists = new ArrayList<String>(); )。最后通过wordsCount存储单词计数信息(Map<String, Integer> wordsCount = new TreeMap<String,Integer>();)
代码说明
主方法类用于读取文本文件统计字符个数 并调用Store类中的函数将words、lines等参数的值存入
result.txt文件中:
package main.java;
import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import lib.java.Store;
public class WordCount {
public static void main(String[] args) throws Exception {
try {
//File file = new File(args[0]);
//String pathname = file.getPath();
Scanner scanner=new Scanner(System.in);
String pathname=scanner.nextLine();
Reader myReader = new FileReader(pathname);
Reader myBufferedReader = new BufferedReader(myReader);
//对文本处理
CharArrayWriter tempStream = new CharArrayWriter();
int i = -1;
do {
if(i!=-1)
tempStream.write(i);
i = myBufferedReader.read();
if(i >= 65 && i <= 90){
i += 32;
}
}while(i != -1);
myBufferedReader.close();
Writer myWriter = new FileWriter(pathname);
tempStream.writeTo(myWriter);
tempStream.flush();
tempStream.close();
myWriter.close();
BufferedReader br = new BufferedReader(new FileReader(pathname));
int characterscount=0;
int wordline = 0;
int wordcount = 0;
List<String> lists = new ArrayList<String>(); //存储过滤后单词的列表
String readLine = null;
while((readLine = br.readLine()) != null){
wordline++;
String[] wordsArr1 = readLine.split("[^a-zA-Z0-9]"); //过滤出只含有字母的单词
characterscount+=readLine.length();
for (String word : wordsArr1) {
if(word.length() != 0){ //去除长度为0的行
while(!(word.charAt(0)>=97&&word.charAt(0)<=122))
{
word = word.substring(1, word.length());
}
if(word.length()>=4) wordcount++;
lists.add(word);
}
}
}
br.close();
Map<String, Integer> wordsCount = new TreeMap<String,Integer>();
//单词的词频统计
for (String li : lists) {
if(wordsCount.get(li) != null){
wordsCount.put(li,wordsCount.get(li) + 1);
}else{
wordsCount.put(li,1);
}
}
Store st= new Store();
st.store_result(wordsCount, wordline, wordcount, characterscount);
}catch (Exception e) {
e.printStackTrace();
}
}
}
用Store类将输出存入result.txt文件中:
1 package lib.java;
2 import java.io.BufferedWriter;
3 import java.io.File;
4 import java.io.FileWriter;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Comparator;
8 import java.util.Map;
9 import java.util.Map.Entry;
10
11 public class Store {
12
13 public static void store_result(Map<String,Integer> oldmap,int wordline,int wordcount,int characterscount){
14 ArrayList<Map.Entry<String,Integer>> list = new ArrayList<Map.Entry<String,Integer>>(oldmap.entrySet());
15 Collections.sort(list,new Comparator<Map.Entry<String,Integer>>(){
16 public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
17 return o2.getValue() - o1.getValue(); //降序
18 }
19 });
20 try {
21 //System.out.println(wordcount);
22 File file = new File("src//result.txt");
23 BufferedWriter bi = new BufferedWriter(new FileWriter(file));
24 bi.write("characters: "+(characterscount+wordline)+"\r\n");
25 bi.write("words: "+wordcount+"\r\n");
26 bi.write("lines: "+wordline+"\r\n");
27 int k=0;
28 for(int i = 0; i<list.size(); i++){
29 if(k>=10)break;
30 if(list.get(i).getKey().length()>3)
31 {
32 bi.write("<"+list.get(i).getKey()+">"+ ": " +list.get(i).getValue()+"\r\n");
33 k++;
34 }
35
36 }
37 bi.close();
38 }catch (Exception e) {
39 e.printStackTrace();
40 }
41
42 }
43 }
用EclEmma进行覆盖率测试:
命令行测试样例:
![](https://images2018.cnblogs.com/blog/1471076/201809/1471076-20180912131538263-1399979581.png)
总结:
一开始没有认真地理解题目意思,更没有系统地分析解题过程。在没有较为完善的计划之前就直接写程序导致走了很多的弯路。以后再做一个项目之前,哪怕这个项目结构不是很复杂,也应该写好项目文档,这样在开发的过程中便能清楚地知道将要完成的模块和更好的完善代码。在测试过程中也能根据文档快速地进行代码优化。