软件工程 作业三
1.要求地址
2.PSP表格
PSP2.1 | 个人开发流程 | 预估耗费时间(分钟) | 实际耗费时间(分钟) |
---|---|---|---|
Planning | 计划 | 5 | 3 |
· Estimate | 明确需求和其他相关因素,估计每个阶段的时间成本 | 5 | 3 |
Development | 开发 | 274 | 308 |
· Analysis | 需求分析 (包括学习新技术) | 60 | 60 |
· Design Spec | 生成设计文档 | 5 | 2 |
· Design Review | 设计复审 | 4 | 2 |
· Coding Standard | 代码规范 | 5 | 5 |
· Design | 具体设计 | 10 | 20 |
· Coding | 具体编码 | 120 | 180 |
· Code Review | 代码复审 | 10 | 9 |
· Test | 测试(自我测试,修改代码,提交修改) | 60 | 30 |
Reporting | 报告 | 40 | 65 |
· | 测试报告 | 30 | 60 |
· | 计算工作量 | 5 | 2 |
· | 并提出过程改进计划 | 3 | 3 |
3.解题思路描述
这次的项目是建立在上一次作业的项目的基础上,在功能上,主要有两个新增的地方:
- 词组统计:能统计文件夹中指定长度的词组的词频
- 自定义输出:能输出用户指定的前n多的单词与其数量
然后在设计上则是新增了GUI界面。
词组统计的话,是使用了多重循环,分层进行判断,在存储方面也是使用了Map进行存储。
自定义输出的解题思路:戳这里戳这里戳这里
GUI界面使用了netbeans以及web来实现。
我对于GUI界面是使用了netBeans来实现,使用了文本框控件,菜单控件等来实现处理文件以及字符处理,通过给不同按钮添加相应事件,以此来实现不同的功能。
web界面的解题解题思路:戳这里戳这里戳这里
4.设计实现过程
一、相关类设计
- WordDeal类,用于存放统计字符数、行数、单词数、词频等方法。
- FileDeal类作为文件处理类,用于读取文件内容以及写入文件。
- Test类,用于进行单元测试。
二、相关函数设计
- FIleDeal类中的方法
- FileToString方法实现读取一个文件,并且返回文件内容,保存在一个字符串中。
- WriteToFile方法实现把统计结果写入指定文件。
- WordDeal类中的方法
- getCharCount()方法实现统计字符数。
- getLineCount()方法实现计算行数。
- getWordCount()方法实现计算单词数。
- getWordFreq()方法实现统计每一个单词的词频。
- sortMap()方法实现对Map中的数据进行排序,先按词频数后按字典顺序。
- ListToArray()方法实现取出词频数前十的数据。
- getWordGroupFreq(int num)方法用于统计长度为num的词组
- ScDeal类中的方法
- 初始化函数scDeal()
- 判断输入函数judge()
- ideal()是-i的处理函数
- mdeal()是-m的处理函数
- ndeal()是-n的处理函数
- odeal()输出到文件函数
- judge()判断各自输入然后调用不同的处理函数
三、GUI界面设计
- netBeans版本:
- wGUI包下的Main类,使用netBeans设计其相关界面布局等。
- Web版本:
- Main.jsp页面,用于基本界面的设计实现
- show.jsp页面,用于表单提交处理界面的实现
5.代码说明
1.getWordGroupFreq(int num)函数
这个函数实现了统计指定长度的词组的词频信息,通过传入词组长度,然后遍历整个文件内容,先判断单词,再进行词组的判断。最后的存储还是使用Map来进行存储,以便于统计输出。
public Map getWordGroupFreq(int num) // 统计词组词频(单词:以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。)
{
wordGroupFreq = new HashMap<String, Integer>();
String t = text;
String[] spWord = t.split("\s+"); // 对字符串进行分词操作
for (int i = 0; i < spWord.length - num + 1; i++) {
String wordG = "";
int isWG = 1;
for (int j = i; j < i + num; j++) {
if (spWord[j].length() < 4) { // 判断是否是单词,若不是,则直接不用考虑这个词组
isWG = 0;
break;
} else {
int flag = 1; // 判断字符串的前四位是否是英文字母
char c;
for (int k = 0; k < 4; k++) {
c = spWord[j].charAt(k);
if (!(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
flag = 0;
}
}
if (flag == 0) {
isWG = 0;
break;
} else {
wordG = wordG + spWord[j] + " ";
}
}
}
if (isWG == 1 && wordG.trim().length() != 0) {
if (wordGroupFreq.get(wordG.trim()) == null) { // 判断之前Map中是否出现过该词组
wordGroupFreq.put(wordG.trim(), 1);
} else {
wordGroupFreq.put(wordG.trim(), wordGroupFreq.get(wordG.trim()) + 1);
}
}
}
return wordGroupFreq;
}
2.GUI界面主要函数
-
打开文件
该函数用于实现菜单控件中打开文件的功能实现。通过选取文件,将文件内容显示到相应的地方。
private void openFileActionPerformed(java.awt.event.ActionEvent evt) { f = new Frame("my window");// 创建窗体对象 f.setBounds(300, 100, 650, 600);// 设置窗体位置和大小 open = new FileDialog(f, "打开", FileDialog.LOAD); open.setVisible(true);//显示打开文件对话框 String dirpath = open.getDirectory();//获取打开文件路径并保存到字符串中。 String fileName = open.getFile();//获取打开文件名称并保存到字符串中 if (dirpath == null || fileName == null)//判断路径和文件是否为空 { return; } else { text.setText(null);//文件不为空,清空原来文件内容。 } File file = new File(dirpath, fileName);//创建新的路径和名称 try { //BufferedReader bufr = new BufferedReader(new FileReader(file));//尝试从文件中读东西 BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream(file), "gb2312")); String line = null;//变量字符串初始化为空 while ((line = bufr.readLine()) != null) { text.append(line + " ");//显示每一行内容 } bufr.close();//关闭文件 } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } }
-
保存文件
该函数用于将结果框中的内容保存为文件。
private void saveFileActionPerformed(java.awt.event.ActionEvent evt) { save = new FileDialog(f, "保存", FileDialog.SAVE); save.setVisible(true);//显示保存文件对话框 String dirpath = save.getDirectory();//获取保存文件路径并保存到字符串中。 String fileName = save.getFile();////获取打保存文件名称并保存到字符串中 if (dirpath == null || fileName == null)//判断路径和文件是否为空 { return;//空操作 } else { file = new File(dirpath, fileName);//文件不为空,新建一个路径和名称 } try { BufferedWriter bw = new BufferedWriter(new FileWriter(file)); String text = result.getText();//获取文本内容 bw.write(text);//将获取文本内容写入到字符输出流 bw.close();//关闭文件 } catch (IOException e1) { //抛出IO异常 e1.printStackTrace(); } }
-
功能函数的调用
这一类的方法是根据点击的不同按钮,对应实现不同的方法,并将结果进行展示。
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) { // TODO add your handling code here: String t = text.getText(); WordDeal wd = new WordDeal(t); // 调用类中的方法获取相应的数值 int charNum = wd.getCharCount(); int wordCount = wd.getWordCount(); int ValidLine = wd.getLineCount(); Map<String, Integer> wordFreq = wd.getWordFreq(); List sortMap = wd.sortMap(wordFreq); String[] wFreq = wd.ListToArray(sortMap); String w = "字符总数:" + charNum + " " + "单词总数" + wordCount + " " + "有效行数" + ValidLine + " 词频统计: "; for (int i = 0; i < wFreq.length; i++) { w = w + wFreq[i] + " "; } result.setText(w); //写入到相应的控件之中 }
6.回归测试
-
对原有函数的回归测试
package junitTest; import static org.junit.Assert.*; import java.io.IOException; import java.util.*; import org.junit.Before; import org.junit.Test; import wCount.*; public class WordDealTest { @Before public void setUp() throws Exception { } @Test public void testGetCharCount() throws IOException {//统计字符数量测试 FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text2.txt"); String text3 = fd.FileToString("text/text3.txt"); String text4 = fd.FileToString("text/text4.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); WordDeal wd3 = new WordDeal(text3); WordDeal wd4 = new WordDeal(text4); int cn1 = wd1.getCharCount(); int cn2 = wd2.getCharCount(); int cn3 = wd3.getCharCount(); int cn4 = wd4.getCharCount(); assertEquals(0, cn1); assertEquals(0, cn2); assertEquals(80, cn3); assertEquals(73, cn4); } @Test public void testGetWordCount() throws IOException {//统计单词数量测试 FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text4.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); int wn1 = wd1.getWordCount(); int wn2 = wd2.getWordCount(); assertEquals(0, wn1); assertEquals(3, wn2); } @Test public void testGetWordFreq() throws IOException {//统计词频测试 Map<String,Integer> ans1 = new HashMap<String,Integer>(); Map<String,Integer> ans2 = new HashMap<String,Integer>(); ans2.put("coverage", 1); ans2.put("yizhishuijiao", 2); FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text4.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); Map<String,Integer> wf1 = wd1.getWordFreq(); Map<String,Integer> wf2 = wd2.getWordFreq(); assertEquals(ans1, wf1); assertEquals(ans2, wf2); } @Test public void testSortMap() throws IOException {//词频排序测试 FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text4.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); Map<String,Integer> wf1 = wd1.getWordFreq(); Map<String,Integer> wf2 = wd2.getWordFreq(); List lis1 = wd1.sortMap(wf1); List lis2 = wd2.sortMap(wf2); //assertEquals(ans1, wf1); //assertEquals(ans2, wf2); } @Test public void testGetLineCount() throws IOException {//统计有效行数测试 FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text4.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); int wn1 = wd1.getLineCount(); int wn2 = wd2.getLineCount(); assertEquals(0, wn1); assertEquals(4, wn2); } @Test public void testListToArray() throws IOException { FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text4.txt"); String text3 = fd.FileToString("text/text5.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); WordDeal wd3 = new WordDeal(text3); Map<String,Integer> wf1 = wd1.getWordFreq(); Map<String,Integer> wf2 = wd2.getWordFreq(); Map<String,Integer> wf3 = wd3.getWordFreq(); List lis1 = wd1.sortMap(wf1); List lis2 = wd2.sortMap(wf2); List lis3 = wd3.sortMap(wf3); String[] s1 = wd1.ListToArray(lis1); String[] s2 = wd2.ListToArray(lis2); String[] s3 = wd3.ListToArray(lis3); } }
测试结果:
-
对新增函数的测试
测试用例:
测试代码:
@Test public void testGetWordGroupFreq() throws IOException {//统计词组测试 FileDeal fd = new FileDeal(); String text6 = fd.FileToString("text/text6.txt"); WordDeal wd6 = new WordDeal(text6); Map<String,Integer> wf6 = wd6.getWordGroupFreq(3); }
测试结果:
7.单元测试
-
对命令行版本项目的单元测试
-
对web版本项目的单元测试
- 对netBeans版本项目的单元测试
8.效能测试
-
对命令行版本项目的效能测试
-
对web版本项目的效能测试
- 对netBeans版本项目的效能测试
9.结对心得
这次的结对编程是针对一个项目进行的改进,因为这次的要求,命令行输入和GUI界面的实现是两个相对独立的版本,所以经过讨论以后,我们的分工基本还是由我写完新增函数以后,由每个人来实现一个版本,相对来说除了在调用函数方面,其他部分还是比较独立的,所以其实并没有很多结对的感觉,但是在最开始讨论的时候,每个人都还没有很清晰的认识这个过程,导致对这个项目各有各的看法,然后讨论的效率还是比较低,花了很长时间才得到的统一。所以这次编程并没有很多的感受到1+1>2的效果