一、地址链接
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()测试:
import java.io.IOException;
import java.util.*; /**
* Class main
* Author: houst
* Date: 2018/9/17
* Time: 15:50
*/
public class Test { /**
* Main Method
* @param args args
* @throws IOException
*/
public static void main(String args[]) throws IOException { while (true) {
Scanner scanner = new Scanner(System.in);
String order= scanner.nextLine();//Input format
//Judging the input format,entering different processing methods.
Jagger jagger = new Jagger();
jagger.JaggerFormat(order); } } }
Test
2、Jagger 类中 JaggerFormat()方法:判断输入格式
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.StringTokenizer; /**
* Jagger Class
* Author: houst
* Date: 2018/9/20
* Time: 20:15
* Judging input
*/
public class Jagger { /**
* Judging the input format, entering different processing methods.
* @param order order
*/
public void JaggerFormat(String order){ //Use stringTokenizer split input format string,deposit in list.
StringTokenizer stringTokenizer = new StringTokenizer(order, " \n");
ArrayList<String> list = new ArrayList<String>();
while (stringTokenizer.hasMoreElements()) {
list.add(stringTokenizer.nextToken());
} //Judging whether the first word in the input format is the same as the item name.
String project = System.getProperty("user.dir");
project = project.substring(project.lastIndexOf('\\') + 1, project.length());
if (!list.get(0).equals(project)) {
System.out.println("Input format error, please input again!");
} else { LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>();
SortMap sortMap = new SortMap(); //Function one or two
if (list.size() == 3) { String fileName = list.get(2);
File file = new File(fileName);//File name or folder name
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//Suffix name //Function one: number of non repeated words in statistical documents
// Judging input format -c File name.txt
if (list.get(1).equals("-c") && suffix.equals("txt")) {
//Determine whether text files exist.
if (file.isFile() && file.exists()) {
//Read text file (.txt), statistics all word frequency, sequential output.
ReadTxt readTxt = new ReadTxt();
map = readTxt.txtToString(file);//Read txt and return to LinkedHashMap type.
sortMap.orderPrint(map);//order output
} else{
System.out.println("There is no such file!");
} //Function two: Specify the file directory, count the number of words
// that are not duplicated in the most advanced text file in the dictionary order.
// Judging input format -f file path
} else if (list.get(1).equals("-f") && !fileName.contains(".")) {
//Determine whether a folder exists.
if (file.isDirectory() && file.exists()) {
//Read the most prioritized text file (.txt) sorted by dictionary in the folder
// and count all word frequencies.
ReadTxt readTxt = new ReadTxt();
map = readTxt.directoryToTxt(file);
if(!map.isEmpty()){
sortMap.sortMap(map);//Word frequency sorting
}
} else System.out.println("No folder exists!");
} else {
System.out.println("Input format error, please input again!");
} //Function three or four
} else if (list.size() == 5) { String fileName = null;//File name or folder name
String num = null;//input n
String type = null;//input -c or -f symbol //Judging input format type:(-c file name.txt -n number) 或 (-f folder name -n number)
if ((list.get(1).equals("-c") && list.get(3).equals("-n")) || (list.get(1).equals("-f") && list.get(3).equals("-n") )) {
type = list.get(1);
fileName = list.get(2);
num = list.get(4);
//Judging input format type:(-n number -c file name.txt) 或 ( -n number -f folder name)
} else if ( (list.get(1).equals("-n")) && (list.get(3).equals("-c"))|| list.get(1).equals("-n") && (list.get(3).equals("-f") )) {
type = list.get(3);
fileName = list.get(4);
num = list.get(2);
} int no = Integer.parseInt(num);//input num = no String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//get suffix name File file = new File(fileName);
//Function three: the most frequent occurrences of the first N words in statistical documents.
//Judging input format -c file name.txt -n number
if (type.equals("-c") && suffix.equals("txt") && num.matches("^[0-9]*$")) {
//Judging whether text files exist.
if (file.isFile() && file.exists()) {
//Read txt and count the top n high-frequency words.
ReadTxt readTxt = new ReadTxt();
map = readTxt.txtToString(file);//Read txt and return to LinkedHashMap type.
sortMap.sortMap(map,no);//Word frequency sorting with numbers.
} else System.out.println("不存在此文件!");
//Function 3: Specify the file catalog, count the first N words in the text file
// with the highest number of non-repetitions in the dictionary order.
// Judging input format -f File path -n number
} else if (type.equals("-f") && !fileName.contains(".") && num.matches("^[0-9]*$")) {
//Judging whether a folder exists.
if (file.isDirectory() && file.exists()) {
//Read the file name in the folder sorted by dictionary first file (. txt),
// and statistics the first n high-frequency words, sorting output
ReadTxt readTxt = new ReadTxt();
map = readTxt.directoryToTxt(file);//read folder
if(!map.isEmpty()){
sortMap.sortMap(map,no);//word frequency sorting with numbers
}
} else System.out.println("No folder exists!");
} else {
System.out.println("Input format error, please input again!");
} }
}
}
}
JaggerFormat
这个方法逻辑需要很清晰,每一步怎么走,会有什么结果,想了很久,也是在这里比较多bug,调试的时间大概是4小时。
3、ReadTxt类中 txtToString():读取txt文件,一行一行读取处理
/**
* Read (.Txt) files with behavior units
* @param file file
* @return return
* Return type:LinkedHashMap
*/
public LinkedHashMap<String,Integer> txtToString(File file) { BufferedReader reader = null;
LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>(); try { String string = null;
reader = new BufferedReader(new FileReader(file)); //Read txt files from line to line
while ((string = reader.readLine()) != null) { //Convert non alphanumeric symbols into spaces, case sensitive,
// and intercept words by spaces.
string = string.replaceAll("[^a-zA-Z0-9]", " ");
string = string.toLowerCase();
StringTokenizer stringTokenizer = new StringTokenizer(string, " "); //Then use the LinkedHashMap to store words and word frequency.
while (stringTokenizer.hasMoreTokens()) { String word = stringTokenizer.nextToken();
//Intercept the first letter of a word. Does a regular expression determine whether it is a number
String first = word.substring(0, 1);
if (!first.matches("[0-9]{1,}")) {
//Statistical word frequency
if (!map.containsKey(word)) {
map.put(word, new Integer(1));
} else {
int newNum = map.get(word).intValue() + 1;
map.put(word, new Integer(newNum));
}
}
}
}
if(map.isEmpty()){
System.out.println("The content of the text file (.txt) is empty.");
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
txtToString
功能一的难点,也是后续功能二、功能三、功能四的重中之重,在这里大概花了2小时。
4、ReadTxt类中 directoryToTxt():读取文件路径,选择按字典排序最靠前的txt文件,进而调用txtToString()读取txt文件内容
/**
* Find the most advanced text file in the folder according to the dictionary order (.txt).
* @param file file
* @return return
*/
public LinkedHashMap<String,Integer> directoryToTxt(File file){ ArrayList<String> list = new ArrayList<String>();
// //方法一 file.list()
// String[] fileList = file.list();
// for(int i=0;i<fileList.length;i++){
// String fileName = file.getPath()+"\\"+fileList[i];
// System.out.println("111"+fileName);
// File mFile = new File(fileName);
// String suffix = fileList[i].substring(fileList[i].lastIndexOf(".") + 1);
// System.out.println("222"+suffix);
// if(!mFile.isDirectory()&&suffix.equals("txt")){
// System.out.println("333"+mFile.getPath());
// list.add(mFile.getPath());
// }
// }
//方法二 file.listFiles()
//list folder
File[] fileList = file.listFiles();
for(int i=0;i<fileList.length;i++){
String fileName = fileList[i].getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
//Is it a .txt file to add txt files to list?
if(!fileList[i].isDirectory()&&suffix.equals("txt")){
list.add(fileList[i].getAbsolutePath());
}
}
LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>();
if(!list.isEmpty()){
Collections.sort(list);
File finallyFile = new File(list.get(0));
map = txtToString(finallyFile);
return map;
}
else {
System.out.println("There is no txt file in this folder!");
return map;
} }
directoryToTxt
功能二功能四的难点,要先读取文件路径,再进行后续操作,在这里大概花了1小时。
5、SortMap类中 orderPrint() : 顺序输出txt文件单词词频
/**
* Function one :output
* txt file order
* @param map map
*/
public void orderPrint(HashMap map){ //HashMap type data is converted to collection type, and map iterator is obtained.
Iterator iterator = map.entrySet().iterator();
System.out.println("total " + map.size());
System.out.println();
while(iterator.hasNext()){
//Instantiate the Map.Entry object, and then output the word frequency.
Map.Entry word = (Map.Entry) iterator.next();
System.out.printf("%-12s",word.getKey());
System.out.printf("%5d\n",word.getValue()); }
System.out.println();
}
orderPrint
6、SortMap类中 sortMap(Map map):按字典顺序输出txt文件单词词频
/**
* Function two : output
* txt files are sorted in dictionaries.
* @param map map
*/
public void sortMap(Map map){ //Remove the words from map and put them in list.
Collection<String> keys = map.keySet();
List<String> list = new ArrayList<String>(keys);
Collections.sort(list); //sort out output frequency by dictionaries
System.out.println("total " + list.size() + " words");
for(int i=0;i<list.size();i++){
System.out.printf("%-12s",list.get(i));
System.out.printf("%5d\n",map.get(list.get(i)));
}
System.out.println();
}
sortMap(Map map)
7、SortMap类中 sortMap(Map map,int num):先按词频排序,再按字典顺序排序
/**
* Function three
* txt file sorting
* @param map map
* @param num num
*/
public void sortMap(Map map,int num){ List<MyMap<String,Integer>> list = new ArrayList<MyMap<String,Integer>>();
//HashMap type data is converted to collection type, and map iterator is obtained.
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()){
//The words in the Map iterator are stored in list by using MyMap class.
MyMap<String,Integer> word = new MyMap<String,Integer>();
String key = (String) iterator.next();
word.setKey(key);
word.setValue((Integer) map.get(key));
list.add(word);
}
//sort
Collections.sort(list,new Compare());
//Output word frequency
System.out.println("Total words is " + list.size());
System.out.println("------------------");
//Output the specified top n word frequency.
for(int i=0;i<num;i++){
MyMap<String,Integer> word = list.get(i);
System.out.printf("%-12s",word.getKey());
System.out.printf("%5d\n",word.getValue());
}
System.out.println();
}
sortMap(Map map,int num)
这三类排序方法对应功能一、功能二、功能三四的排序方法,难点在sortMap(Map map,int num) 。共花了最多时间,一直再改改改,大概15个小时,因为是几天的时间。
8、Compare类中 compare():实现Comparator接口,重写Compare方法,指定排序方法
/**
* override First compare word frequency, the same word frequency, and then follow the dictionary order.
* @param o1 01
* @param o2 02
* @return return
*/
public int compare(MyMap<String, Integer> o1, MyMap<String, Integer> o2) {
if(o1.getValue().equals(o2.getValue())){
return o1.getKey().compareTo(o2.getKey());
}else{
return o2.getValue()-o1.getValue();
}
}
compare
找资料,基本上我需要用什么就去查什么,学会百度关键词也是很重要的,这也是历届学长学姐教我的。在找资料的过程中,去搜寻别人写过的博客,看他们的代码,有涉及到的函数都要去搜索,看看它们的原理是什么,看懂以后,再自己敲一遍,印象深刻+。这也是我为什么写作业这么慢的一个原因吧......
七、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天写完的统计词频小程序,也让我感受到投入做一件事,努力让它变得完美,专注,钻研,这种感觉很好。同时,也让我意识到自己的实力水平远比想象中的低。我也是时候好好提升自学、写代码的能力了,勿好高骛远,踏踏实实走好每一步,加油!