Github链接:https://github.com/hizxk/PersonProject-Java2
作业链接:https://edu.cnblogs.com/campus/fzu/FZUSoftwareEngineering1816W/homework/2085
psp表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 10 | 20 |
Development | 开发 | 220 | 340 |
• Analysis | • 需求分析 (包括学习新技术) | 20 | 20 |
• Design Spec | • 生成设计文档 | 10 | 10 |
• Design Review | • 设计复审 | 15 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 20 |
• Design | • 具体设计 | 30 | 45 |
• Coding | • 具体编码 | 210 | 220 |
• Code Review | • 代码复审 | 20 | 25 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 30 |
Reporting | 报告 | 10 | 20 |
• Test Repor | • 测试报告 | 20 | 20 |
• Size Measurement | • 计算工作量 | 10 | 15 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 660 | 735 |
解题思路描述
这道题有四个需求:
- 统计字符个数
思路:文本字符可以一个一个读,每读一个就加一 统计单词个数
思路:将字符串分割,分割出整个字符串的单词字符串:
1.是可以根据分隔符进行分割,再对分割完的字符串数组进行判断,前四个字符需为英文。2.是采用正则表达式,先创建满足条件的正则表达式,让系统去匹配
- 统计行数
思路:可以一行一行地读取数据,把一行数据保存在String类中,在判断字符串是否含有有效字符 统计文件中各单词的出现次数,找出频率最高的10个并按字典排序输出
思路:单词的查找与统计单词个数思想相同,每个单词及其数量可以用map类来存储,map类本身是通过key的 ascill 值来排序,想通过 values 值来排序,可以将map复制到List类中,通过List排序,找出词频最高的10个单词
设计实现过程
实现语言采用java,程序有两个类,一个是Main.java,另一个是lib.java,Main.java是程序的入口,文件写入和打印函数,以及一个文本编码判断函数,lib.java包含一些函数例如字符数计算,单词个数计算,单词统计等,lib.java中的函数全部为静态,因此不需要实例化lib对象,直接使用类.方法
即可调用类中的方法,由于功能模块互相独立,因此每调用一次函数,都要读取一次文件。Main.java调用lib.java中的函数并输入计算出的值
字符数计算流程图
单词查找流程图
代码说明
- 文本编码判断
java在读取文件时需要对文本的编码进行判断,否则可能出现乱码
下表是java编码与txt编码对应表
java编码 | txt编码 |
---|---|
unicode | unicode big endian |
utf-8 | utf-8 |
utf-16 | unicode |
gb2312 | ANSI |
文本编码会在文本前三个字节说明,因此只要判断前三个字节就可以知道对应的编码
byte[] head = new byte[3];
String code = "gb2312";
try {
InputStream inputStream = new FileInputStream(file);
inputStream.read(head);
if (head[0] == -1 && head[1] == -2) {
code = "UTF-16";
} else if (head[0] == -2 && head[1] == -1) {
code = "Unicode";
} else if (head[0] == -17 && head[1] == -69 && head[2] == -65) {
code = "UTF-8";
}
- 字符个数统计
文件读入通过字节流方式读入 ,每读入一个字符,判断是否是属于ascill字符,满足条件字符数就加一,读取完成的条件为reader.read() != -1
BufferedReader readFile = null;
int countchar = 0;
try {
InputStream is = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(is, code);
readFile = new BufferedReader(isr);
while ((readFile.read()) != -1) {
// if (charTemp / (0x80) == 0) {// 判断是否属于ascill码
countchar++;
// }
}
readFile.close();
- 单词统计
首先使用String类中的split函数对字符串进行分割,[^a-zA-Z0-9]+
指非英文字符和数字字符,接着使用matches()函数匹配满足单词条件的单词,[a-zA-Z]
匹配26个英文字符,{4}
指匹配四个英文字符,{a-zA-Z0-9}
用来匹配英文和数字字符,*
指匹配0个或多个
BufferedReader readFile = null;
String line = null;
int countword = 0;
try {
InputStream is = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(is, code);
readFile = new BufferedReader(isr);
while ((line = readFile.readLine()) != null) {
String[] words = line.split("[^a-zA-Z0-9]+");
for (String word : words) {
if (word.matches("[a-zA-Z]{4}[a-zA-Z0-9]*")) {
countword++;
}
}
}
readFile.close();
isr.close();
is.close();
- 词频最高的十个单词
使用map类来保存单词及其数目Map<String, Integer> TheNumberOfWord = new HashMap<String, Integer>();
,跟计算单词个数函数一样,构造正则表达式然后匹配符合的字符串,每找到一个先用containsKey
函数查找是否已经存在,存在就将values值加一,不存在就置一
while ((line = readFile.readLine()) != null) {
String[] words = line.split("[^a-zA-Z0-9]+");// 以非英文字符和数字为分隔符
for (String word : words) {
if (word.matches("[a-zA-Z]{4}[a-zA-Z0-9]*")) {// 判断是否满足前四个字符为单词的条件
word = word.toLowerCase();
if (TheNumberOfWord.containsKey(word)) {
TheNumberOfWord.put(word, TheNumberOfWord.get(word) + 1);
} else {
TheNumberOfWord.put(word, 1);
}
}
}
}
最后是排序查找,将map类的值保存到list类中,再重写比较器进行比较
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(TheNumberOfWord.entrySet());
Comparator<Entry<String, Integer>> com = new Comparator<Entry<String, Integer>>() {
@Override
public int compare(Entry<String, Integer> arg0, Entry<String, Integer> arg1) {
// TODO Auto-generated method stub
if (arg0.getValue() != arg1.getValue()) {
return (arg1.getValue().compareTo(arg0.getValue()));
} else {
return (arg0.getKey().compareTo(arg1.getKey()));
}
}
};
Collections.sort(list, com);
接口性能改进
- 文本读取到字符串使用 StringBuffer 类取代String,String类是字符串常量,StringBuffer是字符串常量,写入数据速度上来看,StringBuffer大于String,如果是字符串变量的话,尽量用stringbuffer
- 使用BufferedReader类读取文本数据,读取文本数据瓶颈在IO,采用BufferedReader类将数据读取到内存,减少IO操作,提高数据读取速度
单元测试
通过部分单元测试,暂时未找到问题
性能测试
测试软件使用Eclipse Memory Analyzer(这部分不是很懂,只是做了查看)
运行代码覆盖率
异常处理的代码占用一些
程序异常处理
程序在运行过程中或多或少会出现一些异常,目前查找到的异常有
异常名称 | Exception |
---|---|
文件不存在异常 | FileNotFoundException |
文件读写异常 | IOException |
对于异常的发生,采用java异常处理机制:
try{
可能发生异常的代码块
}catch(可以捕获的异常1){
处理异常1的代码
}catch(可以捕获的异常2){
处理异常2的代码
}finally{
处理完所有异常后一定会执行的代码。
在这里注意的事。如果在这里没有出现异常,最终也会执行这行代码
}
捕获到异常就会在控制台打印异常信息:
运行测试
input文件内容
运行结果
心路历程与收获
- 这次实践学到很多,了解程序读写文件的方法,程序异常处理,关键类的使用方法。同时也感受到程序设计和准备十分重要。
- 这次单词匹配使用了正则表达式,最近编译原理有学到一些,感觉很有用处,学以致用
- java在读取文件时最好先判断文本的编码方式,txt文件有四种编码unicode big endian,utf-8,unicode,ANSI,对于不同的编码方式,java也需要采取不同的读取方式
- 还有适当添加些注释,这个习惯很重要,代码量多,添加一些注释,代码结构更加清晰