个人项目

软件工程 网工1934链接
作业要求 作业要求链接
作业目标 完成论文查重项目,进行测试并使用Github进行版本发布及源码和测试用例管理

个人作业github链接

一、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 880 1050
Development 开发
· Analysis · 需求分析 (包括学习新技术) 240 280
· Design Spec · 生成设计文档 60 75
· Design Review · 设计复审 30 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 150 240
· Coding · 具体编码 80 100
· Code Review · 代码复审 100 75
· Test · 测试(自我测试,修改代码,提交修改) 100 120
Reporting 报告
· Test Repor · 测试报告 30 60
· Size Measurement · 计算工作量 30 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 30
· 合计 880 1050

二、模块接口的设计与实现过程

1、主要实现类

主类:

Main:包含main方法,接收命令行输入的参数,调用其他工具类来执行程序。

工具类:

FileUtil:文件工具类。控制文件的读写以及获取文件名。

HammingUtil:计算海明距离的工具类。计算两个simhash值的海明距离以及根据海明距离计算其相似度。

SimHashUtil:计算simhash值的工具类。计算每个单词、整个文本、的simhash值。

测试类:

test:测试主类和工具类是否可以正常使用

项目结构:

image

外部库:

image

各类之间的关系:

image

2、关键函数的分析与实现

分析:关键在于如何对文章进行分词,并计算其simHash值。

实现:

主要过程:分词->计算hash->加权->合并->降维

分词这一步我是使用了外部的依赖包hancks包

  1. 分词,把需要判断文本分词形成这个文章的特征单词。最后形成去掉噪音词的单词序列并为每个词加上权重,我们假设权重分为5个级别(1~5)。比如:“ 美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里是代表单词在整个句子里重要程度,数字越大越重要。
  2. hash,通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字,要把文章变为数字计算才能提高相似度计算性能,现在是降维过程进行时。
  3. 加权,通过第2步骤的hash生成结果,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。
  4. 合并,把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4+-5 -4+5 4+-5 -4+5 4+5” ==》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实计算需要把所有单词的序列串累加。
  5. 降维,把第4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。 如果每一位大于0 记为 1,小于0 记为 0。最后算出结果为:“1 0 1 0 1 1”。

过程图:

image

参考:海量数据相似度计算之simhash和海明距离

三、模块接口部分的性能改进

image

消耗最大的函数是和hacks包相关的函数,毕竟分词这一步是关键,也是操作最多的。

四、模块部分单元测试展示

main类测试

private String origin = ".\\files\\orig.txt";
private String[] txts = {
    ".\\files\\orig_0.8_add.txt",
    ".\\files\\orig_0.8_del.txt",
    ".\\files\\orig_0.8_dis_1.txt",
    ".\\files\\orig_0.8_dis_10.txt",
    ".\\files\\orig_0.8_dis_15.txt",
};
private String target = "./files/ans.txt";

/**
 * 随便测试一个文件
*/
@Test
public void oneText() {
    new Main().main(new String[]{origin, txts[0], target});
}

测试结果:

image

image

FileUtil类测试

 /**
  * 测试读取原文文章
  */
@Test
public void readTxt(){
    //路径存在
    String txt = FileUtil.readFile("./files/orig.txt");
    String[] strings = txt.split(" ");
    for(String str : strings){
        System.out.println(str);
    }
}

测试结果:读取到文章内容

image

/**
 * 测试读取不存在的文章
 */
@Test
public void readFailTxt(){
    //路径不存在
    String txt = FileUtil.readFile("./files/11312.txt");
    String[] strings = txt.split(" ");
    for(String str : strings){
        System.out.println(str);
    }
}

测试结果:image

/**
 * 测试写入内容到不存在的文件
 */
@Test
public void writeFailTxt(){
    // 路径不存在
    String text = "我是网络工程4班的刘浩斌";
    FileUtil.writeFile(text, "user./fail.txt");
}

测试结果:image

HammingUtil类测试

/**
 * 测试两个【不等位数】的海明距离和相似度
 */
@Test
public void testHamming2() {
    String str0 = "1101110"; //000001101110
    String str1 = "110101010110";
    int distance = HammingUtil.getHammingDistance(str0, str1);
    double similarity = HammingUtil.getSimilarity(str0, str1);
    System.out.println("str0和str1的海明距离:" + distance);
    System.out.println("str0和str1的相似度:" + similarity);
}

测试结果:高位补0后,再进行比较。

image

SimHashUtil类测试

/**
 * 测试单词的分词及其hash值
 */
@Test
public void getHashTest() {
    String strs = "我是网络工程4班的刘浩斌";
    List<String> keywordList = HanLP.extractKeyword(strs, strs.length());
    for (String s : keywordList) {
        String stringHash = SimHashUtil.getHash(s);
        System.out.println("【" + s + "】的hash值:" + stringHash);
    }
}

测试结果:
image

/**
 * 测试一段文本的分词情况及最后的hash值
 */
@Test
public void getSimHashTest(){
    String str0 = "活着前言" +
    "\n" +
    "    一位真正的作家永远只为内心写作,只有内心才会真实地告诉他,他的自私、他的高尚是多么突出。内心让他真实地了解自己,一旦了解了自己也就了解了世界。很多年前我就明白了这个原则,可是要捍卫这个原则必须付出艰辛的劳动和长时期的痛苦,因为内心并非时时刻刻都是敞开的,它更多的时候倒是封闭起来,于是只有写作,不停地写作才能使内心敞开,才能使自己置身于发现之中,就像日出的光芒照亮了黑暗,灵感这时候才会突然来到。\n" ;
    System.out.println(SimHashUtil.getSimHash(str0));
}

测试结果:image

/**
 * 测试一段【空】文本的分词情况及最后的hash值
 */
@Test
public void getSimHashEmptyTest(){
    String str0 = "";
    System.out.println(SimHashUtil.getSimHash(str0));
}

测试结果:报“文章无内容”的错

image

代码覆盖率

image

考虑到了一些异常情况以及正常的情况,代码覆盖率几乎100%。当然也有可能一些功能没考虑到哈。

五、模块部分异常处理说明

当读取的文章内容为空时,要计算simHash值时直接抛出【文章无内容】错,退出程序。

if(text.equals("")){
	throw new Error("文章无内容");
}

六、项目程序功能测试

image

image

posted @ 2021-09-18 19:53  xhw2333  阅读(91)  评论(0编辑  收藏  举报