一、地址链接
1、作业地址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2110
2、git仓库地址:https://git.coding.net/kefei101/wf.git
二、需求分析
通过对题目要求的分析,我共看到了以下需求:
!判断输入格式,共有6种输入格式,分别进入4种不同的处理方法,得到不同的排序输出。其中(3)(4)可算作一种,(5)(6)可算作一种。
(1)wf -c 文件名 进入直接读取txt方法,并顺序输出词频;
(2)wf -f 文件路径 进入读取文件夹的方法,再读取按照字典排序最靠前的txt文件,并按照字典排序输出单词词频;
(3)wf -c 文件名 -n 数量 进入直接读取txt方法,并按照词频和字典排序输出;
(4)wf -n 数量 -c 文件名 进入直接读取txt方法,并按照词频和字典排序输出;
(5)wf -f 文件路径 -n 数量 进入读取文件夹的方法,再读取按照字典排序最靠前的txt文件,并按照词频和字典排序输出;
(6)wf -n 数量 -f 文件路径 进入读取文件夹的方法,再读取按照字典排序最靠前的txt文件,并按照词频和字典排序输出.
三、功能设计
1、根据用户输入的参数格式,读取指向的文件或文件路径,统计得到txt文本文件的单词词频结果。
2、此处实现附加功能:在小程序中,可以任意输入多次,永久判断;输入格式中的首位单词 wf 即是保存该小程序的文件夹名,若将其放入不同的文件夹,只需将 wf 变为文件夹名即可。
注:为了避免中文乱码问题,我将注释及程序提示都改成英文显示。
四、设计实现
我共设计6个类,如图:
Test类:主类,负责接收输入格式的参数启动程序;
Jagger类:负责根据输入格式的参数不同,分别进入不同的处理方法;
ReadTxt类:负责读取txt文本文件,以及读取文件路径;
SortMap类:负责根据需求的跟能不同,进入不同的排序方法;
Compare类:实现Comparator接口,实现功能(3)--(6)的排序方式(先按照词频排序,再按照字典排序);
MyMap类:是SortMap类中功能三所需要用到的类,用自定义Map类实现list与Map转换的简单性。
5个类的相互关系为:
比较重要的函数有:
Jagger类 :JaggerFormat():判断输入格式,主要是逻辑
ReadTxt类:txtToString():读取txt文件,一行一行读取处理
directoryToTxt():读取文件路径,选择按字典排序最靠前的txt文件,进而调用txtToString()读取txt文件内容
SortMap类:orderPrint():顺序输出txt文件单词词频
sortMap(Map map):按字典顺序输出txt文件单词词频
sortMap(Map map,int num):先按词频排序,再按字典顺序排序
Compare类: compare():实现Comparator接口,重写Compare方法,指定排序方法
函数间的逻辑关系:JaggerFormat()----[directoryToTxt()]----txtToString()----orderPrint()/sortMap(Map map)/[sortMap(Map map,int num)----compare()]
五、测试运行
1、输入正确的格式,即可运行功能一二三
2、测试测试txt
六、重要代码展示
1、主方法main()测试:
1 import java.io.IOException; 2 import java.util.*; 3 4 /** 5 * Class main 6 * Author: houst 7 * Date: 2018/9/17 8 * Time: 15:50 9 */ 10 public class Test { 11 12 /** 13 * Main Method 14 * @param args args 15 * @throws IOException 16 */ 17 public static void main(String args[]) throws IOException { 18 19 while (true) { 20 Scanner scanner = new Scanner(System.in); 21 String order= scanner.nextLine();//Input format 22 //Judging the input format,entering different processing methods. 23 Jagger jagger = new Jagger(); 24 jagger.JaggerFormat(order); 25 26 } 27 28 } 29 30 }
2、Jagger 类中 JaggerFormat()方法:判断输入格式
1 import java.io.File; 2 import java.util.ArrayList; 3 import java.util.LinkedHashMap; 4 import java.util.StringTokenizer; 5 6 /** 7 * Jagger Class 8 * Author: houst 9 * Date: 2018/9/20 10 * Time: 20:15 11 * Judging input 12 */ 13 public class Jagger { 14 15 16 /** 17 * Judging the input format, entering different processing methods. 18 * @param order order 19 */ 20 public void JaggerFormat(String order){ 21 22 //Use stringTokenizer split input format string,deposit in list. 23 StringTokenizer stringTokenizer = new StringTokenizer(order, " "); 24 ArrayList<String> list = new ArrayList<String>(); 25 while (stringTokenizer.hasMoreElements()) { 26 list.add(stringTokenizer.nextToken()); 27 } 28 29 //Judging whether the first word in the input format is the same as the item name. 30 String project = System.getProperty("user.dir"); 31 project = project.substring(project.lastIndexOf('\') + 1, project.length()); 32 if (!list.get(0).equals(project)) { 33 System.out.println("Input format error, please input again!"); 34 } else { 35 36 LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>(); 37 SortMap sortMap = new SortMap(); 38 39 //Function one or two 40 if (list.size() == 3) { 41 42 String fileName = list.get(2); 43 File file = new File(fileName);//File name or folder name 44 String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//Suffix name 45 46 //Function one: number of non repeated words in statistical documents 47 // Judging input format -c File name.txt 48 if (list.get(1).equals("-c") && suffix.equals("txt")) { 49 //Determine whether text files exist. 50 if (file.isFile() && file.exists()) { 51 //Read text file (.txt), statistics all word frequency, sequential output. 52 ReadTxt readTxt = new ReadTxt(); 53 map = readTxt.txtToString(file);//Read txt and return to LinkedHashMap type. 54 sortMap.orderPrint(map);//order output 55 } else{ 56 System.out.println("There is no such file!"); 57 } 58 59 //Function two: Specify the file directory, count the number of words 60 // that are not duplicated in the most advanced text file in the dictionary order. 61 // Judging input format -f file path 62 } else if (list.get(1).equals("-f") && !fileName.contains(".")) { 63 //Determine whether a folder exists. 64 if (file.isDirectory() && file.exists()) { 65 //Read the most prioritized text file (.txt) sorted by dictionary in the folder 66 // and count all word frequencies. 67 ReadTxt readTxt = new ReadTxt(); 68 map = readTxt.directoryToTxt(file); 69 if(!map.isEmpty()){ 70 sortMap.sortMap(map);//Word frequency sorting 71 } 72 } else System.out.println("No folder exists!"); 73 } else { 74 System.out.println("Input format error, please input again!"); 75 } 76 77 //Function three or four 78 } else if (list.size() == 5) { 79 80 String fileName = null;//File name or folder name 81 String num = null;//input n 82 String type = null;//input -c or -f symbol 83 84 85 //Judging input format type:(-c file name.txt -n number) 或 (-f folder name -n number) 86 if ((list.get(1).equals("-c") && list.get(3).equals("-n")) || (list.get(1).equals("-f") && list.get(3).equals("-n") )) { 87 type = list.get(1); 88 fileName = list.get(2); 89 num = list.get(4); 90 //Judging input format type:(-n number -c file name.txt) 或 ( -n number -f folder name) 91 } else if ( (list.get(1).equals("-n")) && (list.get(3).equals("-c"))|| list.get(1).equals("-n") && (list.get(3).equals("-f") )) { 92 type = list.get(3); 93 fileName = list.get(4); 94 num = list.get(2); 95 } 96 97 int no = Integer.parseInt(num);//input num = no 98 99 String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//get suffix name 100 101 File file = new File(fileName); 102 //Function three: the most frequent occurrences of the first N words in statistical documents. 103 //Judging input format -c file name.txt -n number 104 if (type.equals("-c") && suffix.equals("txt") && num.matches("^[0-9]*$")) { 105 //Judging whether text files exist. 106 if (file.isFile() && file.exists()) { 107 //Read txt and count the top n high-frequency words. 108 ReadTxt readTxt = new ReadTxt(); 109 map = readTxt.txtToString(file);//Read txt and return to LinkedHashMap type. 110 sortMap.sortMap(map,no);//Word frequency sorting with numbers. 111 } else System.out.println("不存在此文件!"); 112 //Function 3: Specify the file catalog, count the first N words in the text file 113 // with the highest number of non-repetitions in the dictionary order. 114 // Judging input format -f File path -n number 115 } else if (type.equals("-f") && !fileName.contains(".") && num.matches("^[0-9]*$")) { 116 //Judging whether a folder exists. 117 if (file.isDirectory() && file.exists()) { 118 //Read the file name in the folder sorted by dictionary first file (. txt), 119 // and statistics the first n high-frequency words, sorting output 120 ReadTxt readTxt = new ReadTxt(); 121 map = readTxt.directoryToTxt(file);//read folder 122 if(!map.isEmpty()){ 123 sortMap.sortMap(map,no);//word frequency sorting with numbers 124 } 125 } else System.out.println("No folder exists!"); 126 } else { 127 System.out.println("Input format error, please input again!"); 128 } 129 130 } 131 } 132 } 133 }
这个方法逻辑需要很清晰,每一步怎么走,会有什么结果,想了很久,也是在这里比较多bug,调试的时间大概是4小时。
3、ReadTxt类中 txtToString():读取txt文件,一行一行读取处理
1 /** 2 * Read (.Txt) files with behavior units 3 * @param file file 4 * @return return 5 * Return type:LinkedHashMap 6 */ 7 public LinkedHashMap<String,Integer> txtToString(File file) { 8 9 BufferedReader reader = null; 10 LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>(); 11 12 try { 13 14 String string = null; 15 reader = new BufferedReader(new FileReader(file)); 16 17 //Read txt files from line to line 18 while ((string = reader.readLine()) != null) { 19 20 //Convert non alphanumeric symbols into spaces, case sensitive, 21 // and intercept words by spaces. 22 string = string.replaceAll("[^a-zA-Z0-9]", " "); 23 string = string.toLowerCase(); 24 StringTokenizer stringTokenizer = new StringTokenizer(string, " "); 25 26 //Then use the LinkedHashMap to store words and word frequency. 27 while (stringTokenizer.hasMoreTokens()) { 28 29 String word = stringTokenizer.nextToken(); 30 //Intercept the first letter of a word. Does a regular expression determine whether it is a number 31 String first = word.substring(0, 1); 32 if (!first.matches("[0-9]{1,}")) { 33 //Statistical word frequency 34 if (!map.containsKey(word)) { 35 map.put(word, new Integer(1)); 36 } else { 37 int newNum = map.get(word).intValue() + 1; 38 map.put(word, new Integer(newNum)); 39 } 40 } 41 } 42 } 43 if(map.isEmpty()){ 44 System.out.println("The content of the text file (.txt) is empty."); 45 } 46 } catch (IOException e) { 47 e.printStackTrace(); 48 } 49 return map; 50 }
功能一的难点,也是后续功能二、功能三、功能四的重中之重,在这里大概花了2小时。
4、ReadTxt类中 directoryToTxt():读取文件路径,选择按字典排序最靠前的txt文件,进而调用txtToString()读取txt文件内容
1 /** 2 * Find the most advanced text file in the folder according to the dictionary order (.txt). 3 * @param file file 4 * @return return 5 */ 6 public LinkedHashMap<String,Integer> directoryToTxt(File file){ 7 8 ArrayList<String> list = new ArrayList<String>(); 9 // //方法一 file.list() 10 // String[] fileList = file.list(); 11 // for(int i=0;i<fileList.length;i++){ 12 // String fileName = file.getPath()+"\"+fileList[i]; 13 // System.out.println("111"+fileName); 14 // File mFile = new File(fileName); 15 // String suffix = fileList[i].substring(fileList[i].lastIndexOf(".") + 1); 16 // System.out.println("222"+suffix); 17 // if(!mFile.isDirectory()&&suffix.equals("txt")){ 18 // System.out.println("333"+mFile.getPath()); 19 // list.add(mFile.getPath()); 20 // } 21 // } 22 //方法二 file.listFiles() 23 //list folder 24 File[] fileList = file.listFiles(); 25 for(int i=0;i<fileList.length;i++){ 26 String fileName = fileList[i].getName(); 27 String suffix = fileName.substring(fileName.lastIndexOf(".") + 1); 28 //Is it a .txt file to add txt files to list? 29 if(!fileList[i].isDirectory()&&suffix.equals("txt")){ 30 list.add(fileList[i].getAbsolutePath()); 31 } 32 } 33 LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>(); 34 if(!list.isEmpty()){ 35 Collections.sort(list); 36 File finallyFile = new File(list.get(0)); 37 map = txtToString(finallyFile); 38 return map; 39 } 40 else { 41 System.out.println("There is no txt file in this folder!"); 42 return map; 43 } 44 45 }
功能二功能四的难点,要先读取文件路径,再进行后续操作,在这里大概花了1小时。
5、SortMap类中 orderPrint() : 顺序输出txt文件单词词频
1 /** 2 * Function one :output 3 * txt file order 4 * @param map map 5 */ 6 public void orderPrint(HashMap map){ 7 8 //HashMap type data is converted to collection type, and map iterator is obtained. 9 Iterator iterator = map.entrySet().iterator(); 10 System.out.println("total " + map.size()); 11 System.out.println(); 12 while(iterator.hasNext()){ 13 //Instantiate the Map.Entry object, and then output the word frequency. 14 Map.Entry word = (Map.Entry) iterator.next(); 15 System.out.printf("%-12s",word.getKey()); 16 System.out.printf("%5d ",word.getValue()); 17 18 19 } 20 System.out.println(); 21 }
6、SortMap类中 sortMap(Map map):按字典顺序输出txt文件单词词频
1 /** 2 * Function two : output 3 * txt files are sorted in dictionaries. 4 * @param map map 5 */ 6 public void sortMap(Map map){ 7 8 //Remove the words from map and put them in list. 9 Collection<String> keys = map.keySet(); 10 List<String> list = new ArrayList<String>(keys); 11 Collections.sort(list); 12 13 //sort out output frequency by dictionaries 14 System.out.println("total " + list.size() + " words"); 15 for(int i=0;i<list.size();i++){ 16 System.out.printf("%-12s",list.get(i)); 17 System.out.printf("%5d ",map.get(list.get(i))); 18 } 19 System.out.println(); 20 }
7、SortMap类中 sortMap(Map map,int num):先按词频排序,再按字典顺序排序
1 /** 2 * Function three 3 * txt file sorting 4 * @param map map 5 * @param num num 6 */ 7 public void sortMap(Map map,int num){ 8 9 List<MyMap<String,Integer>> list = new ArrayList<MyMap<String,Integer>>(); 10 //HashMap type data is converted to collection type, and map iterator is obtained. 11 Iterator iterator = map.keySet().iterator(); 12 while (iterator.hasNext()){ 13 //The words in the Map iterator are stored in list by using MyMap class. 14 MyMap<String,Integer> word = new MyMap<String,Integer>(); 15 String key = (String) iterator.next(); 16 word.setKey(key); 17 word.setValue((Integer) map.get(key)); 18 list.add(word); 19 } 20 //sort 21 Collections.sort(list,new Compare()); 22 //Output word frequency 23 System.out.println("Total words is " + list.size()); 24 System.out.println("------------------"); 25 //Output the specified top n word frequency. 26 for(int i=0;i<num;i++){ 27 MyMap<String,Integer> word = list.get(i); 28 System.out.printf("%-12s",word.getKey()); 29 System.out.printf("%5d ",word.getValue()); 30 } 31 System.out.println(); 32 }
这三类排序方法对应功能一、功能二、功能三四的排序方法,难点在sortMap(Map map,int num) 。共花了最多时间,一直再改改改,大概15个小时,因为是几天的时间。
8、Compare类中 compare():实现Comparator接口,重写Compare方法,指定排序方法
1 /** 2 * override First compare word frequency, the same word frequency, and then follow the dictionary order. 3 * @param o1 01 4 * @param o2 02 5 * @return return 6 */ 7 public int compare(MyMap<String, Integer> o1, MyMap<String, Integer> o2) { 8 if(o1.getValue().equals(o2.getValue())){ 9 return o1.getKey().compareTo(o2.getKey()); 10 }else{ 11 return o2.getValue()-o1.getValue(); 12 } 13 }
找资料,基本上我需要用什么就去查什么,学会百度关键词也是很重要的,这也是历届学长学姐教我的。在找资料的过程中,去搜寻别人写过的博客,看他们的代码,有涉及到的函数都要去搜索,看看它们的原理是什么,看懂以后,再自己敲一遍,印象深刻+。这也是我为什么写作业这么慢的一个原因吧......
七、PSP
sp2.1 | 任务内容 | 计划共完成需要时间(min) | 实际完成需要的时间(min) |
Planing | 计划 | 2160 | 4080 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 2160 | 3480 |
Development | 开发 | 1860 | 3660 |
Analysis | 需求分析 | 300 | 480 |
Design Spec | 生成设计文档 | 0 | 0 |
Design Review | 设计复审(和同事审核设计文档) | 0 | 0 |
Coding Standard | 代码规范()为目前的开发制定合适的规范 | 0 | 0 |
Design | 具体设计 | 300 | 480 |
Coding | 具体编码 | 900 | 1200 |
Code Review | 代码复审 | 120 | 300 |
Test | 测试(自我测试,修改代码,提交修改) | 240 | 1200 |
Reporting | 报告 | 300 | 420 |
Test Report | 测试报告 | 180 | 300 |
Size Measurement Postmortem & Process Improvement Plan |
计算工作量,事后总结,并提出过程改进计划 | 120 | 120 |
功能模块 | 具体阶段 | 预计时间(min) | 实际时间(min) |
功能一 |
具体设计 具体编码 测试完善 |
30 120 150 |
80 150 400 |
功能二 |
具体设计 具体编码 测试完善 |
120 200 300 |
180 450 500 |
功能三 |
具体设计 具体编码 测试完善 |
150 200 290 |
220 600 600 |
以上只是我大概估计,因为是好几天才做出来的,难免有些不准确,但通过上面两个表格对比,能够看出,我的写代码能力真的是...... 效率太低下啦!这样子的人,有哪个公司敢要呀?即使是期间夹杂着自学,理解函数的时间,也不应该花这么多时间做作业呀。再次证明,缺实践!!!
八、总结
这次项目说实在,说简单也不简单,说难也不难,原本以为一两天就可以搞定,实际上花了整整4天时间做这个小程序。其实做这个项目我并没有完整按照构造之法的软件开发步骤去做。但在看到这个作业的时候,我会先看有什么要求,想了很多怎么分类,怎么构造方法,怎么让它们联系起来。所以我是在前期花了快8个小时的构思。接着我便开始上手敲代码,先从本类开始,判断输入格式。根据输入格式的不同,我先写直接处理txt文件的方法,把第一个功能实现,慢慢的再把第二个功能实现,一步步做下去。期间因为看着作业的布置,有些细节的地方没有注意到,导致到后面代码是改了又改,写了又写,深刻体会到程序员的痛苦(一旦用户的需求改变,就会内心奔腾吧)。
通过做这次作业,我其实感触挺大的。在之前,我一直认为,啊,我待在工作室里做了几个小项目,挺厉害的,这次作业那么简单,我肯定一两天就写完了。但其实我在做作业写代码的时间要比其他人多得多。不得不说,在实力上,我受到了一定的打击。其实我不比别人好多少,甚至别人会比我更加努力,更加厉害。
在做这个作业的时候,我其实是想实现更多的功能的,以此证明自己,也就想着慢慢写,每天写一点,总能写完,一定要细心,把作业完美的展现出来。这种想法是不对的。看我在截至时间最后一秒交作业就知道了,效率会很低下,没有紧凑感,又怎么能锻炼自己写代码的能力呢?现在这种小程序还好说,不会有什么损失。如果是以后到了公司,让你写个app,写个系统啥的,你也能保证最后交给甲方的是最完美的?所以,我要改变我的看法了,写代码这种事,是不能拖拉的。我们应该在有限的时间内写出高效代码。
最后,我想说,虽然做作业拖拉了,但总体还是比较完美的,自己花了4天写完的统计词频小程序,也让我感受到投入做一件事,努力让它变得完美,专注,钻研,这种感觉很好。同时,也让我意识到自己的实力水平远比想象中的低。我也是时候好好提升自学、写代码的能力了,勿好高骛远,踏踏实实走好每一步,加油!