结对项目完成WordCount

合作者:201631062507  201631062526(学号)

代码地址:https://gitee.com/WordCountMC/WordCountTeam

本次作业链接:https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188

1.结对项目PSP编写


我们的项目于10月11日正式开始,在开始编写代码之前,我们先对我们如何完成项目以及对老师布置的作业等进行再一次的商定和讨论。并结合我们平时上课的时间以及项目外等因素,约定了我们一起进行结对编程的时间和地点。最后一起完成了PSP表格的填写:

PSP2.1

PSP阶段

预估耗时(分钟)

实际耗时(分钟)

Planning 计划  30  30
· Estimate · 估计这个任务需要多少时间  30  30
Development 开发  620  945
· Analysis · 需求分析 (包括学习新技术)  30  40
· Design Spec · 生成设计文档  20  30
· Design Review · 设计复审 (和同事审核设计文档)  30  60
· Coding Standard · 代码规范 (为目前的开发制定合适的规范)  30  40
· Design · 具体设计  40  45
· Coding · 具体编码  350  500
· Code Review · 代码复审  60  100
· Test · 测试(自我测试,修改代码,提交修改)  60  130
Reporting 报告  80  80
· Test Report · 测试报告  30  30
· Size Measurement · 计算工作量  20  10
· Postmortem & Process Improvement Plan .事后总结并提出改进计划  30  40
  合计  730  1055

总结分析:这次在项目完成后,发现预估时间和实际耗时相差比较大,其主要表现在开发阶段。由于我们在代码自审和互审阶段,都发现了编码所存在的不规范,所以花了更多的时间去修改代码。还有就是在编码的时候,我们要完成的内容比我们想象得要困难一点,所以在编码的时候花了更多的时间。最后就是测试,这次项目开发采用了TDD,我们要先编写测试代码,再实现接口,这与我们之间采用的方式也不同,所以也花了更多的时间。

2.代码互审


2.1总体情况分析

在审查之后发现,队友的代码已经根据了我们一起编写的小组编程规范进行了修改。修改后的代码风格简洁明了,非常容易理解和阅读。但是仍需注意的地方是,方法里的代码注释要写在对应代码的上面而不是后面,这样会使代码阅读更加清晰。除此之外,在互审之后,发现代码存在一个小bug需要进行改正。

2.2 问题

出现问题的模块在CountUtil类中的returnWords方法。

问题是在测试时发现:当一个文件为空的时候,它会返回单词数为1,而且当有两个文件同时测试时,如果第一个文件的单词数比第二个文件的单词数多的话,只显示第一个的单词数。

2.3  解决方法

在问题提出后,我和队友一起解决了问题,发现问题出在每次统计单词数目所要用的StringBuffer没有进行清空的操作。

3.设计


3.1 结构设计

功能类:主函数类(WordCount类) 接口类(CountUtil类)

WordCount类:

CountUtil类:

3.2 类间关系

3.3 WordCount类的流程图

4.代码说明


4.1扩展功能:

(1)当输入-e时,后面跟停用词表stoplist.txt

首先解析停用词表中的单词:

    /**
     * 用来解析停用词表中单词
     * **/
    public String[] stoplistWord(String stopList)
    {
        String lineString = null;
        String[] stopListBuffer = null;
        File sl = new File(stopList);
            try {
                BufferedReader br = new BufferedReader(new FileReader(sl));
                while((lineString = br.readLine())!=null) {
//当遇到空格的时候,就为一个单词
                    stopListBuffer = lineString.split("\\s+");
                }
                br.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
                
        return stopListBuffer;
    }

然后判断文件中的单词是否是停用词表中的单词:

    /***
*
* 判断该词是否存在于停用词表内
* */
public static boolean inStop(String str, String[] fileBuffer)
{
int count = 0;
for(int i = 0;i<fileBuffer.length;i++) {
//将获得的单词,与停用词表中的单词一个一个的与进行比较
if(str.equals(fileBuffer[i])) {
count++;
}
}
if(count > 0) {
return true;
}else
return false;
}

(2)当输入-a时,查询文件中的代码行数、注释行数、空行数

getCodeLines() 函数:

        /**
* 查询文件的代码行数
* @param filePaths 所有有效文件的集合
* @return String类型 查询结果
*/
public String getCodeLines(Set<String> filePaths) {
// 结果字符串
String result = "";
// 缓存变量
String tmp = "";
// 统计次数
int count = 0;
// 得到输入流
FileInputStream is = null;
InputStreamReader ist = null;
BufferedReader br = null;
try {
for (String filePath : filePaths) {
is = new FileInputStream(filePath);
ist = new InputStreamReader(is);
br = new BufferedReader(ist);
while((tmp = br.readLine())!=null) {
// 去除读取的空格,方便识别内容类型
tmp = tmp.replace(" ", "");
// 改行不为空,则计数+1
if (!"".equals(tmp)&&!tmp.startsWith("//")&&tmp.indexOf("//")!=1) {
count++;
}
}
// 拼接结果字符串
result+=filePath+CODE_LINES+count+SPACE;
// 重置count计数变量
count = 0;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭输入流
is.close();
ist.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} }
return result;
}

getEmptyLines() 函数:

        /**
* 查询文件的空行数
* @param filePaths 所有有效文件的集合
* @return String类型 查询结果
*/
public String getEmptyLines(Set<String> filePaths) {
// 结果字符串
String result = "";
// 缓存变量
String tmp = "";
// 统计次数
int count = 0;
// 得到输入流
FileInputStream is = null;
InputStreamReader ist = null;
BufferedReader br = null;
try {
for (String filePath : filePaths) {
is = new FileInputStream(filePath);
ist = new InputStreamReader(is);
br = new BufferedReader(ist);
while((tmp = br.readLine())!=null) {
// 改行不为空,则计数+1
if ("".equals(tmp)) {
count++;
}
}
// 拼接结果字符串
result+=filePath+EMPTY_LINES+count+SPACE;
// 重置count计数变量
count = 0;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭输入流
is.close();
ist.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} }
return result;
}

getNoteLines() 函数:

        /**
* 查询文件的注释行数
* @param filePaths 所有有效文件的集合
* @return String类型 查询结果
*/
public String getNoteLines(Set<String> filePaths) {
// 结果字符串
String result = "";
// 缓存变量
String tmp = "";
// 统计次数
int count = 0;
// 得到输入流
FileInputStream is = null;
InputStreamReader ist = null;
BufferedReader br = null;
try {
for (String filePath : filePaths) {
is = new FileInputStream(filePath);
ist = new InputStreamReader(is);
br = new BufferedReader(ist);
while((tmp = br.readLine())!=null) {
// 去除读取的空格,方便识别内容类型
tmp = tmp.replace(" ", "");
// 改行不为空,则计数+1
if (!"".equals(tmp)&&tmp.startsWith("//")||tmp.indexOf("//")==1) {
count++;
}
}
// 拼接结果字符串
result+=filePath+NOTE_LINES+count+SPACE;
// 重置count计数变量
count = 0;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭输入流
is.close();
ist.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} }
return result;
}

(3)当输入-s时,递归处理目录下符合条件的文件

    /**
* 得到输入文件中指定文件和指定文件夹下符合条件的文件
* @param names 输入文件的集合
* @param condition 文件夹下的筛选条件
* @return Set<String>类型 所有指定文件和指定文件夹下符合条件的文件
*/
public Set<String> getAllFileWithCondition(Set<String> names,String condition) {
// 存储输入文件和输入文件夹下符合指定条件的文件的集合
Set<String> filePaths = new HashSet<String>();
for (String name : names) {
File file = new File(name);
if (file.isFile()) {
filePaths.add(name);
}else if (file.isDirectory()) {
// 调用处理文件夹下查询符合指定条件的文件
filePaths.addAll(getDirectoryFilesWithCondition(file, condition));
}
}
return filePaths;
}

4.2高级功能:

当输入-x的时候,打开图形化界面操作,调用getUI()方法

/**
* 获得图形界面
*/
public void getUI() {
JFileChooser jFileChooser = new JFileChooser();
JFrame jFrame = new JFrame("文件数据查询");
Container container = jFrame.getContentPane();
container.setLayout(null); JLabel hint = new JLabel("选择文件",Label.RIGHT);
hint.setBounds(10,10,100,30); JTextField name = new JTextField();
name.setBounds(80,10,190, 30);
name.setEditable(false);
name.setFont(new Font("dialog", 1, 15));
name.setBackground(Color.WHITE); JLabel hint1 = new JLabel("输入停用词",Label.RIGHT);
hint1.setBounds(10,58,100,30); JTextField wordList = new JTextField();
wordList.setBounds(80,58,190, 30);
wordList.setFont(new Font("dialog", 1, 15));
wordList.setBackground(Color.white); JLabel hint3 = new JLabel("(单词之间用空格隔开)");
hint3.setBounds(85,85,150,30); JButton confirm = new JButton("确定");
confirm.setBounds(280,58,80,30); JButton choose = new JButton("选择...");
choose.setBounds(280,10,80,30); JLabel hint2 = new JLabel("结果如下:");
hint2.setBounds(10,100,190,30); JTextArea contents = new JTextArea();
contents.setBounds(43,140,300,280); choose.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
int returnVal = jFileChooser.showOpenDialog(null);
if(JFileChooser.APPROVE_OPTION == returnVal) {
filePaths.add(jFileChooser.getSelectedFile().getAbsolutePath());
name.setText(jFileChooser.getSelectedFile().getName());
contents.setText(performOperation());
}
}
}); confirm.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
CountUtil.USE_STOP = true;
CountUtil.putInFile(wordList.getText()+" ", stopListPath);
STOPLIST_FILE = stopListPath;
}
});
container.add(hint);
container.add(name);
container.add(choose);
container.add(contents);
container.add(wordList);
container.add(hint1);
container.add(confirm);
container.add(hint2);
container.add(hint3);
jFrame.setSize(400, 500);
jFrame.setVisible(true); }

getUI()方法代码

5.测试过程


   5.1测试模块建立

此次测试使用的是java提供的单元测试工具进行测试,新建了一个CountUtilTest类,对CountUtil中的接口方法进行测试。在此次单元测试的设计中,我们所设计的测试用例包括了没有内容的.c文间,只有一个单词的.c文件等比较典型的测试用例。

    private CountUtil countUtil = new CountUtil();
@org.junit.Test
public void testReturnCharacters() {
Set<String> filePaths = new HashSet<String>();
String File1 = "F:\\out.c";
filePaths.add(File1);
filePaths =countUtil.getAllFilePaths(filePaths);
String result = "F:\\out.c,字符数:46 ";
assertTrue("字符数错误",result.equals(countUtil.returnCharacters(filePaths)));
} @org.junit.Test
public void testReturnWords() {
Set<String> filePaths = new HashSet<String>();
String File1 = "F:\\test.java";
String File2 = "F:\\WordCount";
String stopList = "";
filePaths.add(File2);
filePaths.add(File1);
filePaths =new CountUtil().getAllFilePaths(filePaths);
String result = "F:\\WordCount\\result.txt,单词数:35 F:\\WordCount\\wc.exe,单词数:256100 "
+ "F:\\WordCount\\AimResult.txt,单词数:76 F:\\test.java,单词数:3 "
+ "F:\\WordCount\\ddd\\hello.txt,单词数:2 ";
assertTrue("单词数错误", result.equals(countUtil.returnWords(filePaths, stopList)));
} @org.junit.Test
public void testReturnLines() {
Set<String> filePaths = new HashSet<String>();
String File1 = "F:\\test.java";
String File2 = "F:\\WordCount";
filePaths.add(File2);
filePaths.add(File1);
filePaths =countUtil.getAllFilePaths(filePaths);
String result = "F:\\WordCount\\result.txt,行数 :3 F:\\WordCount\\wc.exe,行数 :3042 "
+ "F:\\WordCount\\AimResult.txt,行数 :3 F:\\test.java,行数 :1 "
+ "F:\\WordCount\\ddd\\hello.txt,行数 :1 ";
assertTrue("行数错误", result.equals(countUtil.returnLines(filePaths)));
} @org.junit.Test
public void testOutPutFile() {
String FilePath = "result.txt";
String Content = "OutPutFile test !";
assertTrue(countUtil.outPutFile(FilePath, Content));
} @org.junit.Test
public void testGetAllFilePaths() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
String File1 = "F:\\WordCount";
String FIle2 = "F:\\out.c";
String result1 = "F:\\WordCount\\result.txt";
String result2 = "F:\\out.c";
String result3 = "F:\\WordCount\\wc.exe";
String result4 = "F:\\WordCount\\AimResult.txt";
String result5 = "F:\\WordCount\\ddd\\hello.txt";
filePaths.add(File1);
filePaths.add(FIle2);
resultPaths.add(result1);
resultPaths.add(result2);
resultPaths.add(result3);
resultPaths.add(result4);
resultPaths.add(result5);
filePaths = countUtil.getAllFilePaths(filePaths);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetDirectoryFiles() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
File File1 = new File("F:\\WordCount");
String file1 = "F:\\WordCount\\result.txt";
String file2 = "F:\\WordCount\\wc.exe";
String file3 = "F:\\WordCount\\AimResult.txt";
String file4 = "F:\\WordCount\\ddd\\hello.txt";
resultPaths.add(file1);
resultPaths.add(file2);
resultPaths.add(file3);
resultPaths.add(file4);
filePaths = CountUtil.getDirectoryFiles(File1);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetAllFileWithCondition() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
Set<String> Files = new HashSet<String>();
String File1 = "F:\\WordCount";
String FIle2 = "F:\\out.c";
Files.add(FIle2);
Files.add(File1);
String condition = ".c";
String result = "F:\\out.c";
resultPaths.add(result);
filePaths = new CountUtil().getAllFileWithCondition(Files, condition);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetDirectoryFilesWithCondition() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
File File1 = new File("F:\\WordCount");
String condition = ".c";
filePaths = CountUtil.getDirectoryFilesWithCondition(File1, condition);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetCodeLines() {
Set<String> filePaths = new HashSet<String>();
filePaths.add("F:\\out.c");
String result = "F:\\out.c,代码行数:2 ";
assertTrue("代码行错误", result.equals(countUtil.getCodeLines(filePaths)));
} @org.junit.Test
public void testGetEmptyLines() {
Set<String> filePaths = new HashSet<String>();
filePaths.add("F:\\out.c");
String result = "F:\\out.c,空行数:1 ";
assertTrue("空行数错误", result.equals(countUtil.getEmptyLines(filePaths)));
} @org.junit.Test
public void testGetNoteLines() {
Set<String> filePaths = new HashSet<String>();
filePaths.add("F:\\out.c");
String result = "F:\\out.c,注释行数:2 ";
assertTrue("注释行错误", result.equals(countUtil.getNoteLines(filePaths)));
}

CountUtilTest()单元测试代码

5.2 单元测试结果

5.3 exe文件测试结果

所有的文件:

out.c用例:

(1)代码行数、注释行数、空行数测试:

(2)单词数、行数、字符数测试

(3)字符数、行数、单词数和代码数、注释行数、空行数测试:

(4)基础功能和扩展功能测试

(5)停用词表测试

(6)UI界面

6.总结


       在这次结对项目实践完成之后,我发现1+1是大于2的。我们项目完成的效率远比当时我一个人完成的时候要高出很多,而且我们一起编程可以及时解决所遇到的问题和避免一些不必要的问题。避免走了很多的弯路。除此之外,我还学习到了我队员的编程思想和他的学习方式,这些东西都给我带来了很多的启发和新的思路。总得来说,整个项目的完成也比之前的更加顺利和充满动力。提高了我对完成项目的积极性。

系统分析与设计结对项目——WordCount的更多相关文章

  1. 系统分析与设计个人作业:WordCount

    本次作业gitee地址:https://gitee.com/ackary/WordCount 一.项目简介 1.基础功能 基础功能部分主要实现的功能是统计一个程序设计语言源文件的字符数.单词数.行数, ...

  2. 结对项目-WordCount

    结对作业: 成员:201631062115(me),201631062613(partner) 代码地址:https://gitee.com/ackary/WordCount 作业的链接地址:http ...

  3. 四则运算生成器功能完善&&界面设计——结对项目

    结对成员:何小松 && 李入云 一.对结对编程的认识 优点: 1)程序员互相帮助,互相教对方,可以得到能力上的互补. 2)可以让编程环境有效地贯彻Design. 3)增强代码和产品质量 ...

  4. WordCount 2.0(结对项目)

    序言 合作伙伴 201631062220      201631062120 项目码云地址: https://gitee.com/zhege/WordCount 作业详细要求 系统分析与设计结对项目 ...

  5. WordCount结对项目

    合作者:201631062124,201631062423 代码地址:https://gitee.com/yryx/WordCount 作业地址:https://edu.cnblogs.com/cam ...

  6. 结对项目——Core设计与实现

    写在前面:关于结对编程 结对编程我一直认为是一种非常好的合作方式,他的形式主要是由一个人负责代码编写,另一个人则在一旁即时对写下的代码进行审查,这样可以大大减少代码实现方面的错误. 这次我的结对伙伴是 ...

  7. 结对作业——WordCount进阶版

    Deadline: 2018-10-7 22:00PM,以博客提交至班级博客时间为准 要求参考来自:https://www.cnblogs.com/xinz/archive/2011/11/27/22 ...

  8. 软工结对项目之词频统计update

    队友 胡展瑞 031602215 作业页面 GitHub 具体分工 111500206 赵畅:负责WordCount的升级,添加新的命令行参数支持(自定义输入输出文件,权重词频统计,词组统计等所有新功 ...

  9. 结对作业-WordCount进阶版

    1.在文章开头给出博客作业要求地址. 博客园地址:https://www.cnblogs.com/happyzm/p/9559372.html 2.给出结对小伙伴的学号.博客地址,结对项目的码云地址. ...

随机推荐

  1. javascript 数组和对象的浅复制和深度复制 assign/slice/concat/JSON.parse(JSON.stringify())

    javascript 数组和对象的浅度复制和深度复制在平常我们用 ‘=’来用一个变量引用一个数组或对象,这里是‘引用’而不是复制下面我们看一个例子引用和复制是什么概念 var arr=[1,2,3,' ...

  2. Redis ZSet 有序集合

    有序集合类型与集合类型的区别就是他是有序的.有序集合是在集合的基础上为每一个元素关联一个分数,这就让有序集合不仅支持插入,删除,判断元素是否存在等操作外,还支持获取分数最高/最低的前N个元素.有序集合 ...

  3. FromServices回来

    FromServices回来 起因 这两天,我忽然有点怀念 Asp.NET MVC 5 之前的时代,原因是我看到项目里面有这么一段代码(其实不止一段,几乎每个 Controller 都是) [Rout ...

  4. 使用自定义Comparator对TreeSet中的数据进行多条件排序

    代码记录(需求:根据店铺等级和店铺到某个点的距离进行排序,其中店铺等级由高到低,距离由近及远) 需要排序的对象Store,Store.java package com.zhipengs.work.te ...

  5. 微信小程序获取windowHeight出现不同页面高度不一致问题及解决方案

    在页面中调用wx.getSystemInfo即可获取当前页面的屏幕高度,如果写在app.js里面或者带有系统tab页面里面,获取的windowHeight会比不是tab的页面少48: 含有的TabBa ...

  6. rebbitMQwindows安装及使用

    python中RabbitMQ的使用(安装和简单教程) 1,简介 RabbitMQ(Rabbit Message Queue)是流行的开源消息队列系统,用erlang语言开发.   1.1关键词说明: ...

  7. 2、head 标签学习

     5秒自动刷新 <meta http-equiv="refresh" content="5,url:http://www.baidu.com" /> ...

  8. 了解 DML、DDL、DCL

    一.DML DML(Data Manipulation Language)数据操纵语言: 最经常用到的 SELECT.UPDATE.INSERT.DELETE. 主要用来对数据库的数据进行一些操作 S ...

  9. Docker下搭建mongodb副本集

    背景 有需求需要对mongodb做一个容灾备份.根据官网,发现mongodb最新版本(4.0)已经抛弃了主从模式而采用副本集进行容灾.副本集的优势在于:"有自动故障转移和恢复特性,其任意节点 ...

  10. JAVA MAC 比较大小

    2个MAC String mac_1="AAAAAAAAAAAA"; String mac_1="AAAAAAAAAAAB"; 方法一: int a = 0; ...