个人项目
软件工程 | 网工1934链接 |
---|---|
作业要求: 1.在Github仓库中新建一个学号为名的文件夹 2.在开始实现程序之前,在PSP表格记录下在程序开发各个步骤耗费时间,实现程序后,在PSP表格记录各个模块上实际花费时间 3.语言不限,实现程序后将代码发布到Github仓库的realease中 4.提交的代码要求经过Code Quality Analysis工具分析并消除所有警告 5.完成项目首个版本之后,使用性能分析工具Studio Profiling Tools找出代码性能瓶颈 6.使用Github来管理源代码和测试用例,代码有进展即签入Github 7.使用单元测试对项目进行测试,并使用插件查看测试分支覆盖率等指标 |
作业要求链接 |
作业目标: 完成论文查重项目的实现后进行测试并按以上要求使用Github进行版本发布及源码和测试用例管理 |
一.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 405 | 340 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 100 | 120 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 40 | 40 |
· Coding | · 具体编码 | 80 | 40 |
· Code Review | · 代码复审 | 60 | 20 |
· Test | · 测试(自我测试,修改代码,提交修改) | 20 | 15 |
Reporting | 报告 | ||
· Test Repor | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 15 | 15 |
· 合计 | 405 | 340 |
二. 模块接口的设计与实现过程
1. 主要实现类
工具类:
FileUtils:文件工具类,具有读写文件,获取文件名等静态方法
QueryReapeat :查重工具类,具有对两段字符串进行查重等静态方法
ResultEditor类:结果处理工具类,具有对查重结果进行处理并返回的静态方法
主类:
Application 类 :程序的入口类,其中的main方法可以接收多个参数用来作为输入输出文件的绝对路径,main方法中调用工具类中的静态方法实现整个查重功能。
各类之间的关系:
项目结构:
2. 关键函数的分析与实现
关键函数在于如何实现查重函数queryRepeat()?
通过上网查阅资料,参考了TF-IDF与余弦相似性的应用(二):找出相似文章 这篇博文。
假设有两个句子A,B
句子A:我喜欢吃苹果,不喜欢吃榴莲。
句子B:我不喜欢吃苹果,也不喜欢吃榴莲。
第一步,分词。
句子A:我/喜欢/吃/苹果,不/喜欢/吃/榴莲。
句子B:我/不/喜欢/吃/苹果,也/不/喜欢/吃/榴莲。
实现:通过IKAnalyzer中文分词器实现,即通过查询内置的词典进行分词
第二步,列出所有的词。
我,喜欢,吃,苹果,榴莲,不,也。
第三步,计算词频。
句子A:我 1,喜欢 2,吃 2,苹果 1,榴莲 1,不 1,也 0。
句子B:我 1,喜欢 2,吃 2,苹果 1,榴莲 1,不 2,也 1。
第四步,写出词频向量。
句子A:[1, 2, 2, 1, 1, 1, 0]
句子B:[1, 2, 2, 1, 1, 2, 1]
实现:词与词频是键值对的形式,因此可以用哈希表来存储
到这里,问题就变成了如何计算这两个向量的相似程度。
我们可以把它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为0度,意味着方向相同、线段重合;如果夹角为90度,意味着形成直角,方向完全不相似;如果夹角为180度,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。
则计算A, B向量间的夹角的余弦值
假定a向量是[x1, y1],b向量是[x2, y2],那么可以将余弦定理改写成下面的形式:
推广至n维
余弦值越接近1,说明两向量间夹角越接近0,即两个句子越相似,因此余弦值即为两句子的相似度
三.模块接口部分的性能改进
可以看出程序中消耗最大的函数是中文分词器查询字典调用的相关函数
四.模块部分单元测试展示
1.测试工具
使用JUnit4进行单元测试,创建一个TestCoverage类,在其中测试各模块方法。
2.WordsUtil工具类方法测试
@Test
public void testWordUtil() {
// 测试正常情况下分词
List<String> strings1 = WordsUtil.splitWords("今天是周天,天气晴朗,我晚上要去看电影。");
// 测试空串情况下分词
List<String> strings2 = WordsUtil.splitWords("");
// 测试字符串中插入不同符号情况下分词
List<String> strings3 = WordsUtil.splitWords("@*()-+今天是**周天,天气晴朗");
// 测试字符串中存在空格情况下分词
List<String> strings4 = WordsUtil.splitWords(" 今天是周天,天气晴 朗,我晚上要去看电影。 ");
System.out.println("strings1:");
for (String string : strings1) {
System.out.print(string + "/");
}
System.out.println();
System.out.println("strings2:");
for (String string : strings2) {
System.out.print(string + "/");
}
System.out.println();
System.out.println("strings3:");
for (String string : strings3) {
System.out.print(string + "/");
}
System.out.println();
System.out.println("strings4:");
for (String string : strings4) {
System.out.print(string + "/");
}
System.out.println();
}
测试结果:
3. QueryRepeat工具类测试
@Test
public void testQueryRepeat() {
// 完全倒序情况
double repeatRadius1 = QueryRepeat.getRepeatRadius("今天是星期天,天气晴,今天晚上我要去看电影。",
"影电看去要我上晚天今,晴气天,天期星是天今。");
// 空字符串情况
double repeatRadius2 = QueryRepeat.getRepeatRadius("", "");
// 完全相同情况
double repeatRadius3 = QueryRepeat.getRepeatRadius("今天是星期天,天气晴,今天晚上我要去看电影。",
"今天是星期天,天气晴,今天晚上我要去看电影。");
// 部分抄袭情况
double repeatRadius4 = QueryRepeat.getRepeatRadius("今天是星期天,天气晴,今天晚上我要去看电影。",
"今天是周天,天气晴朗,我晚上要去看电影。");
// 插入符号情况
double repeatRadius5 = QueryRepeat.getRepeatRadius("今天是星期天,天气晴,今天晚上我要去看电影。",
"今天是*&星期*(天,天气+-晴,今天晚&&上我要去看电影。");
// 包含空格情况
double repeatRadius6 = QueryRepeat.getRepeatRadius("今天是星期天,天气晴,今天晚上我要去看电影。",
"今天是 星期 天,天气 晴,今天晚 上我要去看电影。");
System.out.println("repeatRadius1:" + repeatRadius1);
System.out.println("repeatRadius2:" + repeatRadius2);
System.out.println("repeatRadius3:" + repeatRadius3);
System.out.println("repeatRadius4:" + repeatRadius4);
System.out.println("repeatRadius5:" + repeatRadius5);
System.out.println("repeatRadius6:" + repeatRadius6);
}
空串情况下:
剩余情况:
4.代码覆盖率截图
五.模块部分异常处理说明
通过上面单元测试发现了QueryRepeat.getRepeatRadius("", "")方法会报空指针异常并对其进行了捕获处理,在控制台输出了相应报错信息。
也可以在Application类加入判断,当有空串时就退出程序并打印信息到控制台
if(originText.equals("") || copyText.equals("")) {
System.out.println("文件内无内容!");
System.exit(0);
}
六. 项目程序功能测试