一、地址链接

  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个类,如图:

软件工程 week 02-LMLPHP

  Test类:主类,负责接收输入格式的参数启动程序;

  Jagger类:负责根据输入格式的参数不同,分别进入不同的处理方法;

  ReadTxt类:负责读取txt文本文件,以及读取文件路径;

  SortMap类:负责根据需求的跟能不同,进入不同的排序方法;

  Compare类:实现Comparator接口,实现功能(3)--(6)的排序方式(先按照词频排序,再按照字典排序);

  MyMap类:是SortMap类中功能三所需要用到的类,用自定义Map类实现list与Map转换的简单性。

  5个类的相互关系为:

  软件工程 week 02-LMLPHP

  比较重要的函数有:

    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、输入正确的格式,即可运行功能一二三

 软件工程 week 02-LMLPHP

 软件工程 week 02-LMLPHP

  软件工程 week 02-LMLPHP

  2、测试测试txt

软件工程 week 02-LMLPHP


六、重要代码展示

  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计划21604080
Estimate估计这个任务需要多少时间,并规划大致工作步骤21603480
Development开发18603660
Analysis需求分析300480
Design Spec生成设计文档00
Design Review设计复审(和同事审核设计文档)00
Coding Standard代码规范()为目前的开发制定合适的规范00
Design具体设计300480
Coding具体编码9001200
Code Review代码复审120300
Test测试(自我测试,修改代码,提交修改)2401200
Reporting报告300420
Test Report测试报告180300

Size Measurement

Postmortem & Process

Improvement Plan

计算工作量,事后总结,并提出过程改进计划120120
功能模块具体阶段预计时间(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天写完的统计词频小程序,也让我感受到投入做一件事,努力让它变得完美,专注,钻研,这种感觉很好。同时,也让我意识到自己的实力水平远比想象中的低。我也是时候好好提升自学、写代码的能力了,勿好高骛远,踏踏实实走好每一步,加油!

05-11 12:54