编程作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/zswxy/computer-science-class4-2018/ |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/zswxy/computer-science-class4-2018/homework/11880 |
这个作业的目标 | 能够实现统计文本文档词频的控制台程序,进行代码优化,完成代码完成词频统计个人作业,实现要求。 |
其他参考文献 | 《构建之法》、《现代软件工程》、CSDN、博客园、Git |
1、项目链接(https://gitee.com/kckr/project-java/tree/master/20188527)
2、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 15 | 20 |
Development | 开发 | 400 | 460 |
• Analysis | • 需求分析 (包括学习新技术) | 40 | 60 |
• Design Spec | • 生成设计文档 | 20 | 20 |
• Design Review | • 设计复审 | 10 | 15 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 25 |
• Design | • 具体设计 | 20 | 20 |
• Coding | • 具体编码 | 200 | 220 |
• Code Review | • 代码复审 | 30 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 70 |
Reporting | 报告 | 70 | 90 |
• Test Repor | • 测试报告 | 45 | 60 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 15 | 20 |
合计 | 485 | 570 |
3、解题思路描述
仔细阅读完作业题目要求后,发现题目要实现6个功能,具体需求为:
1.读取txt文件中的内容
2.统计文件的字符数
3.统计文件的单词总数
4.统计统计文件的有效行数
5.统计文件中各单词的出现次数,并输出频率最高的10个
6.将输出结果写入txt文件
分析完需求后,问题首先要解决的就是如何进行文件的读写
针对第二个需求统计文件的字符数,我首先想到的就是采用一个字符一个字符的读取文件内容,这样可以更好的对每一个字符进行判断。而后的几个需求,我认为采用行方式读取文件内容,对于功能的实现会简单些。
对于第三个需求,我的想法是将文件每行的内容采用分割法,以非字母、数字的分割符将其分割保存在数组中,在对每个划分后的词进行判断是否为有效单词,但这个分割的方法一开始让我无从下手,后面在CSDN上查找资料发现了正则表达式,这样单词的分割就简单了许多。
第四个需求也是对文件的每行单独判断,想着尝试将字符串中的空字符以空字符串代替,后面查找资料发现了replaceAll("\s*","")方法。
针对第五个需求,采用同需求三一样的方法先划分单词,然后再将有效单词转换为小写保存在Map中,每次有效单词出现就先判断是否存在,存在则将value+1,否则就存入新的有效单词。词频的统计我是在CSDN上查找了Map<String,Integer>的排序方法,并且修改排序规则从而实现的。
4、代码规范制定链接
myCodeStyle(https://gitee.com/kckr/project-java/blob/master/20188527/src/codestyle.md)
5、设计与实现过程
根据程序功能要求,划分为一个主函数,两个类
WordCount 主函数
FileIO 实现文件的读取,以及将结果写入文件
DoWordCount 实现字符、单词、函数、单词词频的计算
countChars调用getReader,得到其返回的Reader进行以单个字符形式的文件读取
countWords、countLines、sortWords的参数皆为readFile返回的ArrayList
countWords中在判断是否为有效单词时,调用了isValidWord函数,isValidWord函数中判断单词前四位是否为字母时调用了isAlpha函数
printTop10的参数为sortWords函数Map排序后返回的List<Map.Entry<String, Integer>>
7、关键代码
//统计字符数,空格,水平制表符,换行符,均算字符
while ((tempChar = reader.read()) != -1) {
sum++;
}
在单词分割上,我使用了正则表达式(这应该就是我这代码里面唯一的独到之处了) ,使用非字母和数字来分割文件内容的每一行,在对分割后的单词逐一进行判断,调用isValidWord(word)方法判断其长度是否满足要求,isValidWord(word)方法里面又调用了isAlpha()方法判断前四位是否为字母。
for (int i = 0; i < lines.size(); i++) {
line = lines.get(i);
words = line.split("[^a-zA-Z0-9]+");
for (int j = 0; j < words.length; j++) {
char[] word = words[j].toCharArray();
if (isValidWord(word)) {
sum++;
}
}
}
将文件的每一行中的空白符以空字符串代替,在将其与空字符串比较,若非空,则为有效行。
for (int i = 0; i < lines.size(); i++) {
line = lines.get(i);
line = line.replaceAll("\\s*", "");
if (!(line.equals(""))) {
sum++;
}
}
同前面一样,先判断是否为有效单词,若有效,将其转换为小写,然后使用map进行储存,因为map的key不可重复,所以每次写入前判断map之前是否存在过该数据,若没有存在过,则该value值为1,否则,在其value值上再加1.最后将map改为list存储,再用Collections.sort进行排序,通过重写comparator来实现要求的排序,当单词的数量一致时,则比较单词在字典序的先后。
if (isValidWord(word)) {
tempWord = words[j].toLowerCase();
if (!map.containsKey(tempWord)) {
map.put(tempWord, Integer.valueOf(1));
} else {
map.put(tempWord, Integer.valueOf(map.get(tempWord).intValue() + 1));
}
}
List<Map.Entry<String, Integer>> mappingList = new ArrayList<>(map.entrySet());
//通过比较器实现比较排序
Collections.sort(mappingList, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if (o1.getValue().equals(o2.getValue())) {
return o1.getKey().compareTo(o2.getKey());
} else {
return o2.getValue().compareTo(o1.getValue());
}
}
});
8、异常处理说明
异常处理命令行参数无输入/输出文件的情况:
if(args.length!=2){
System.out.println("参数输入个数有误,请重新输入");
return ;
}
输出文件不存在时,系统会自动创建输出文件。
剩余的输入流异常,抛出到最外面同一处理。
9、心路历程与收获
在这次的作业中,我学到了很多编写代码以外其他的很多内容,也更加体会到了软件工程这门课的重要性。接触了Git和Gitee,并了解了如何使用gitee发布项目和控制版本,不在只是停留在纸面上的认识,一旦项目有进展便签入Gitee,也使我更加深刻感到它带来的好处。在此次的实践中,我也学会了按照PSP表格对项目进行一步一步的构建完成,不像之前一股脑就开始进入编程阶段,按照这样的方式写出来的程序可靠性也要来的强得多。并且在此次的作业中,也严格规范了自己的代码风格,对我来说有又是一次成长。虽然一开始感觉好多东西都在我的知识盲区中,但是每次作业的完成,对未知知识的探索,都是一次极好的体验。