第一次个人编程作业
这个作业属于哪个课程 | 软件工程22级计科12班 |
---|---|
这个作业要求在哪里 | 个人项目 |
这个作业的目标 | 用java实现论文查重的功能,了解并学会软件开发的流程 |
作业github链接点这里
一、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 15 |
Estimate | 估计这个任务需要多少时间 | 30 | 50 |
Development | 开发 | 100 | 150 |
Analysis | 需求分析 (包括学习新技术) | 50 | 60 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审 | 40 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 5 |
Design | 具体设计 | 20 | 30 |
Coding | 具体编码 | 120 | 180 |
Code Review | 代码复审 | 30 | 40 |
Test | 测试(自我测试,修改代码,提交修改) | 50 | 60 |
Reporting | 报告 | 50 | 70 |
Test Repor | 测试报告 | 40 | 40 |
Size Measurement | 计算工作量 | 5 | 20 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 15 | 20 |
合计 | 590 | 800 |
二、项目的设计与实现
1.开发环境
编程语言:java
编程工具:IntelliJ IDEA 2023.1
分词依赖包:hanlp分词包
2.所设计的主要类
txt_in:用于读取文件路径以及文件内容,还有往文件写入结果。
simhash_tranlate:将文章分词,然后对每个关键词用hash值替代,然后再通过加权、合并、降维得到simhash值。
hanming_bijiao:将两个文章得到的simhash通过海明距离算法得出相似度。
paper_check:这是主类,识别路径参数,然后通过上面三个类进行查重得出重复率。
3.运行过程
首先,主类paper_check会将路径参数传给txt_in进行路径比对以及文章读取,当没问题时,就会将所读取的文章通过simhash_tranlate类中的方法以及依赖包hanlp对文章进行分词,然后用SHA-256算法对每个关键词赋予hash值,然后利用256位的特征向量来加权、合并、降维,从而得到两个文章的simhash,然后将这两个值传给hanming_bijiao类通过海明距离算法来求得二者的重复率,然后再通过txt_in类中的方法将结果输出到目标文件中。
4.文件结构
5.实现的关键
首先是使用了hanlp汉语言分词包,这个分词会相对来说更加的准确,且使用了SHA-256加密算法来对关键词进行信息化,然后对于simhash值的表示采用的是经过处理后的256位的二进制当作特征向量来表示一篇文章,从而使本程序在关键词处理上会更加严谨,对于整个设计的实现十分关键。
三、性能分析
使用的是JProfile 14.0.4工具进行性能分析
本部分用的是orig.txt与orig_0.8_add.txt作为例子及进行分析,耗时一秒多一点,符合作业要求
内存使用情况如下:
由图可知,float型参数使用率很高,或许跟SHA-256的加密算法有关,这里若要改进或许可以采用其他的加密算法。然后占内存最大的是int型,因为本程序采用的是256位的整型数组来作为特征向量,这也是意料之中,总内存80MB左右,算是比较常规的内存消耗,后续要改进的话,我打算从simhash算法的信息处理以及海明距离的代码实现进行改进。
四、单元测试
本测试用的是idea里的junit插件,编写了一些测试类来测试正式代码的功能实现。
1.读取文件与写入模块
这里有四项测试,第一个是读取文件路径正常,正常读取文件内容,第二个是输出文件路径正常,正常输出,第三个是读取路径不存在,读取失败,第四个是输出路径不存在,输出失败。
点击可查看代码
@Test
public void readTxtTest() {
// 路径存在,正常读取
String str = txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\test_work.txt");
String[] strings = str.split(" ");
for (String string : strings) {
System.out.println(string);
}
}
@Test
public void writeTxtTest() {
// 路径存在,正常写入
double[] elem = {0.11, 0.22, 0.33, 0.44, 0.55};
for (int i = 0; i < elem.length; i++) {
txt_in.writeTxt(elem[i], "E:\\jk_java\\jk_secondwork_file\\test.txt");
}
}
@Test
public void readTxtFailTest() {
// 路径不存在,读取失败
String str = txt_in.readTxt("D:/test/none.txt");
}
@Test
public void writeTxtFailTest() {
// 路径错误,写入失败
double[] elem = {0.11, 0.22, 0.33, 0.44, 0.55};
for (int i = 0; i < elem.length; i++) {
txt_in.writeTxt(elem[i], "User:/test/ans.txt");
}
}
测试结果如下:
写入成功如下:
2.simhash计算模块
这里的有两个测试点,一个是将一些关键词通过加密算法转换成hash值,另一个是将这个所有关键字的hash值来对特征向量进行处理,从而得出一篇文章的simhash值。
点击可查看代码
@Test
public void gethash_test(){
String[] strs={"今天","天气","晴朗","我们","去","公园","打","乒乓球"};
for(String str : strs){
String str_hash= simhash_tranlate.getHash(str);
System.out.println(str+" 的hash值有"+str_hash.length()+"位");
System.out.println("值为"+str_hash);
}
}
@Test
public void getsimhash_test(){
String str1= txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\orig.txt");
String str2=txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\orig_0.8_add.txt");
System.out.println("原文的simhash为:"+simhash_tranlate.getsimhash_number(str1));
System.out.println("增加版的simhash为:"+simhash_tranlate.getsimhash_number(str2));
}
测试结果如下:
很明显,测试结果是成功的。
3.海明距离算法的实现模块
该模块里包含3个测试,第一个是当两篇文章simhash值不对等时的异常处理,第二个是对于海明距离的计算测试,第三个是对于重复率的计算测试。
点击可查看代码
@Test
public void getHammingDistanceTest() {
String str0 = txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\orig.txt");
String str1 = txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\orig_0.8_add.txt");
int distance = hanming_bijiao.getdistance(simhash_tranlate.getsimhash_number(str0), simhash_tranlate.getsimhash_number(str1));
System.out.println("海明距离:" + distance);
}
@Test
public void getHammingDistanceFailTest() {
// 测试str0.length()!=str1.length()的情况
String str0 = "10101010";
String str1 = "1010101";
System.out.println(hanming_bijiao.getdistance(str0, str1));
}
@Test
public void getSimilarityTest() {
String str0 = txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\orig.txt");
String str1 = txt_in.readTxt("E:\\jk_java\\jk_secondwork_file\\orig_0.8_add.txt");
int distance = hanming_bijiao.getdistance(simhash_tranlate.getsimhash_number(str0), simhash_tranlate.getsimhash_number(str1));
double similarity = hanming_bijiao.similar_number(simhash_tranlate.getsimhash_number(str0), simhash_tranlate.getsimhash_number(str1));
System.out.println("str0和str1的相似度:" + similarity);
}
测试结果如下:
很显然,测试成功了。
4.主类的模块测试
这个测试主要是将原文与要比对的所有文件都进行了测试比对,并写入文件,测试整个流程的完整性与可实行性。
点击可查看代码
@Test
public void origAndAllTest(){
String[] str = new String[6];
str[0] = "E:\\jk_java\\jk_secondwork_file\\orig.txt";
str[1] = "E:\\jk_java\\jk_secondwork_file\\orig_0.8_add.txt";
str[2] = "E:\\jk_java\\jk_secondwork_file\\orig_0.8_del.txt";
str[3] = "E:\\jk_java\\jk_secondwork_file\\orig_0.8_dis_1.txt";
str[4] = "E:\\jk_java\\jk_secondwork_file\\orig_0.8_dis_10.txt";
str[5] = "E:\\jk_java\\jk_secondwork_file\\orig_0.8_dis_15.txt";
String ansFileName = "E:\\jk_java\\jk_secondwork_file\\test.txt";
String[] GOOD=new String[3];
GOOD[0]=str[0];
GOOD[2]=ansFileName;
for(int i = 0; i <= 5; i++){
GOOD[1]=str[i];
paper_check.main(GOOD);
}
}
测试结果如下:
显然,测试很成功。
5.覆盖率
结果如下:
simhash_tranlate类没有覆盖满是因为有一部分是将关键词利用加密算法转换成hash值的异常处理,这里是为了保证代码的安全性添加的,没有太大实际意义。
五、异常处理
本程序的异常处理主要是命令行中输入路径的不正确导致的异常处理,分别是读取文件路径不对与、写入文件路径不对,且都在类txt_in中。
点击可查看代码
public static String readTxt(String txtpath){
String str="";
String line_txt;
//文件按行读入到str中
File file=new File(txtpath);
FileInputStream file_input=null;
try {
file_input=new FileInputStream(file);
InputStreamReader inputStreamReader=new InputStreamReader(file_input,"UTF-8");
BufferedReader reader=new BufferedReader(inputStreamReader);
//字符串拼接
while((line_txt=reader.readLine())!=null){
str+=line_txt;
}
//结束关闭资源
inputStreamReader.close();
reader.close();
file_input.close();
} catch (IOException e) {
System.out.println("读取文件路径出错");
}
return str;
}
public static void writeTxt(double txt_elem,String txt_path){
String str=Double.toString(txt_elem);
File file=new File(txt_path);
FileWriter filewriter=null;
try {
filewriter=new FileWriter(file,true);
filewriter.write(str,0,(str.length()>3?4:str.length()));
filewriter.write("\r\n");
filewriter.close();
} catch (IOException e) {
System.out.println("文件路径有误!未找到指定文件写入");
}
}
结果如下:
六、命令行运行结果
这里是用打包好的jar根据作业要求的方法进行两个文件查重比对。
运行结果如下:
很明显,成功将程序运行的结果写入到目标文件。