第二次作业 论文查重
作业说明(已经提交到git仓库)
课程 | 软件工程2024-双学位(广东工业大学) |
---|---|
题目 | 个人项目作业-论文查重 |
目标 | 代码实现、性能分析、单元测试、异常处理说明、记录PSP表格 |
目录
项目需求
题目:论文查重
描述如下:
设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。
原文示例:今天是星期天,天气晴,今天晚上我要去看电影。
抄袭版示例:今天是周天,天气晴朗,我晚上要去看电影。
要求输入输出采用文件输入输出,规范如下:
从命令行参数给出:论文原文的文件的绝对路径。
从命令行参数给出:抄袭版论文的文件的绝对路径。
从命令行参数给出:输出的答案文件的绝对路径。
注意:答案文件中输出的答案为浮点型,精确到小数点后两位
开发环境
开发语言 | 编辑器 |
---|---|
java | IDEA 2022.1 |
简单查重算法的实现
对比两个句子是否表达一个含义,除开使用语义分析这种需要依赖更加智能的算法外,可以通过简单的对比两个句子的长度、相同的词组数量、相同的字数来简单的对比出两句话是否表达同一个含义,以及两个句子的长度信息。因此设计出下列算法步骤:
1.统计两个字符相同字数的数量占据target的百分比;
2.统计连续相同的字符数量(字符数>=2)占据target的百分比;
3.比较两个字符的长度,如果长度差越大,那么两个字符串一致的概率越小;
项目结构以及核心代码说明
1.org.yby.core.util这个包下存放用到的工具类,AlgorithmUtils是算法的核心实现部分,FileHandUtils是处理文件工具类,TextUtils用来处理文字,MathUtils是一些数学计算工具类;
2.org.yby.core.test这个包下存放的是单元测试;
3.Main这个类是程序入口。
算法实现部分
/**
* 这个类用来设计查重算法需要用到的算法
*/
public final class AlgorithmUtils {
//工具类
private AlgorithmUtils(){}
/**
* 匹配两个字符串的匹配程度
* @param checked 被匹配字符串
* @param target 目标匹配字符串
* @return 两个字符串的匹配程度
*/
public static float fuzzyMatch(String target,String checked){
/* 算法过程 权重取[0.2 0.7 0.1]
1.统计两个字符相同字数的数量占据target的百分比
2.统计连续相同的字符数量(字符数>=2)占据target的百分比
3.比较两个字符的长度,如果长度差越大,那么两个字符串一致的概率越小
*/
int targetLen = target.length();
int checkedLen = checked.length();
//System.out.println("target总长度:"+targetLen+" checked总长度:"+checkedLen);
float[] items = new float[3];
//统计item1
for (int i = 0; i < targetLen; i++) {
for (int j = 0; j < checkedLen; j++) {
if(target.charAt(i) == checked.charAt(j)){
items[0]++;
break;
}
}
}
//System.out.println("相同字的数量:"+items[0]);
items[0] = items[0] / targetLen;
//统计item2
items[1] = findAllTheSameStrLength(target,checked);
//System.out.println("相同词组的数量:"+items[1]);
items[1] = items[1] / targetLen;
//统计item3
items[2] = Math.abs(targetLen - checkedLen) / (float)targetLen;
//标准化
// MathUtils.standardization(items);
// MathUtils.normalization(items);
// System.out.println(Arrays.toString(items));
float rt = items[0] * 0.4f + items[1] * 0.6f - items[2]*0.1f;
return rt<0 ? 0.001f : rt>1 ? 0.99f : rt;
}
/**
* 找出str1与str2所有完全一样的字符子串(每个子串不能再含有子串)
* @return 字符子串数量的总长度
*/
public static int findAllTheSameStrLength(String str1, String str2){
String max = str1.length()>str2.length() ? str1 : str2;
String min = max.equals(str1) ? str2 : str1;
int minIdx=0,maxIdx=0,result=0;
while (minIdx<min.length()){
int temp = minIdx;
if(minIdx+1<min.length() && maxIdx+1<max.length() && min.charAt(minIdx) == max.charAt(maxIdx) && min.charAt(minIdx+1) == max.charAt(maxIdx+1)){
maxIdx+=2; minIdx+=2;
while (minIdx<min.length() && maxIdx<max.length() && min.charAt(minIdx) == max.charAt(maxIdx)){
maxIdx++;minIdx++;
}
}
if(minIdx!=temp){
result+=minIdx-temp;
}
if(++maxIdx > max.length()){maxIdx=minIdx;minIdx++;}
}
return result;
}
}
文件处理
package org.yby.core.utils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* 文件处理工具
*/
public final class FileHandUtils {
private FileHandUtils(){}
public static List<String> readAllLine(String path){
List<String> strs = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
String line;
while ((line = reader.readLine()) != null) {
if(!checkAllNull(line)){ //跳过空行
//System.out.println(line);
strs.add(line.trim());
}
}
} catch (IOException e) {
System.out.println("你输入的文件路径不正确!");
return null;
}
return strs;
}
/**
* 将一个文章按句子分开
* @param path 文件路径
* @return 所有句子
*/
public static String readAllSentence(String path){
List<String> list = readAllLine(path);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Objects.requireNonNull(list).size(); i++) {
sb.append(list.get(i));
}
return sb.toString();
}
public static void write(String content,String path){
try {
// 创建 FileWriter 对象
FileWriter fileWriter = new FileWriter(path);
// 创建 BufferedWriter 对象
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
// 将字符串写入文件
bufferedWriter.write(content);
// 关闭 BufferedWriter
bufferedWriter.close();
} catch (IOException e) {
System.out.println("文件路径错误!");
}
}
/**
* 检查一个字符串是不是全部是空格
*/
public static boolean checkAllNull(String str){
return Pattern.matches("^\\s*$", str);
}
}
单元测试
算法测试
通过测试几组相同的句子来测试算法的可行性,结果表面能够反映一定的真实性。
package org.yby.core.test;
import org.yby.core.utils.AlgorithmUtils;
import org.yby.core.utils.FileHandUtils;
import org.yby.core.utils.MathUtils;
import org.yby.core.utils.TextUtils;
import java.util.HashMap;
/**
* 测试算法的可行性
*/
public class AlgorithmTest {
//测试案例
private static final HashMap<String,String> DEMOS = new HashMap<>();
private static final String originTxtPath = "D:\\Programmings\\IDEA_Programming\\PracticeProjects\\SoftwareHomework\\src\\main\\resources\\orig.txt";
private static final String testTxtPath = "D:\\Programmings\\IDEA_Programming\\PracticeProjects\\SoftwareHomework\\src\\main\\resources\\orig_0.8_add.txt";
private static final String outPutPath = "F:\\Temp\\ans.txt";
private static void initData(){
DEMOS.put(
"小明很高兴地跑到操场上,迎接着他的是一群欢快的同学,他们一起玩耍、笑闹,享受着美好的时光。",
"小明兴致勃勃地跑到操场上,迎接着他的是一群欢快的同学,他们一起嬉戏、欢笑,享受着愉快的时光。");
DEMOS.put("春天来了,万物复苏,花开满园,一派生机勃勃的景象,空气中弥漫着花香和春意。",
"春天到了,大地回春,花朵盛开,一派勃勃生机的景象,空气中弥漫着花香和春天的气息。");
DEMOS.put("海浪拍打着礁石,发出悦耳的声音,海鸥在天空中翱翔,展翅飞翔,构成了一幅美丽的海岸风景。",
"海浪轻拍着礁石,发出悦耳的声响,海鸥在蓝天中飞翔,展翅高飞,呈现出一幅美丽的海岸景色。");
DEMOS.put("夜幕降临,星星点点布满了夜空,月亮悄然升起,洒下一片温柔的光芒,照亮了整个夜晚。",
"夜幕降临,星光闪烁,夜空璀璨夺目,皓月缓缓升起,洒下柔和光芒,照亮了整个夜晚。");
DEMOS.put("林间鸟鸣声声,树影婆娑,清风拂过,带来阵阵清凉,让人感受到大自然的宁静与美好。",
"树林中鸟鸣不断,树叶摇曳,微风拂面,带来一丝清凉,让人沉浸在自然的宁静与美丽之中。");
DEMOS.put("阳光明媚,微风徐来,花香四溢,蝴蝶翩翩起舞,构成了一幅迷人的春日画卷。",
"阳光明媚,微风拂面,花香弥漫,蝴蝶翩翩飞舞,勾勒出一幅迷人的春天景象。");
DEMOS.put("雨后初晴,彩虹横跨天空,七彩斑斓,伴随着微风,显得格外美丽动人。",
"雨后初晴,一道彩虹横跨天际,绚丽多彩,随着微风摇曳,显得格外美丽迷人。");
DEMOS.put("夏日炎炎,绿树成荫,草地上蝉鸣不绝,微风吹过,带来一丝凉爽,让人感受到夏天的热情。",
"夏日酷热,绿荫蔽日,草地上蝉鸣不停,微风拂面,带来一丝清凉,让人体会到夏日的热烈。");
DEMOS.put("秋风瑟瑟,树叶渐黄,落叶飘零,一片金黄色的美景,萦绕在秋日的空气中。",
"秋风凉爽,树叶渐变金黄,落叶飘零,一片金黄色的景象,弥漫在秋日的空气中。");
DEMOS.put("冬日寒冷,白雪皑皑,银装素裹,雪花飘落,营造出一片银白色的冬季景象。",
"冬日严寒,皑皑白雪,银装素裹,雪花纷飞,勾勒出一幅银白色的冬季画面。");
DEMOS.put("今天是星期天,天气晴,今天晚上我要去看电影。",
"今天是周天,天气晴朗,我晚上要去看电影");
}
public static void test1(){
initData();
System.out.println(" 正向检查 反向检查 ");
DEMOS.forEach((k,v)->{
String target = TextUtils.getFullCharacter(k);
String checked = TextUtils.getFullCharacter(v);
System.out.println(" "+AlgorithmUtils.fuzzyMatch(target,checked)+" "+AlgorithmUtils.fuzzyMatch(checked,target));
});
}
public static void test2(){
String target = TextUtils.getFullCharacter(FileHandUtils.readAllSentence(originTxtPath));
String checked = TextUtils.getFullCharacter(FileHandUtils.readAllSentence(testTxtPath));
float rt = AlgorithmUtils.fuzzyMatch(target, checked);
FileHandUtils.write(Float.toString(rt),outPutPath);
System.out.println(rt);
System.out.println(MathUtils.keep2b(rt));
}
}
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 60 |
Estimate | 估计这个任务需要多少时间 | 120 | 150 |
Development | 开发 | 60 | 60 |
Analysis | 需求分析 (包括学习新技术) | 60 | 80 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
Design | 具体设计 | 30 | 20 |
Coding | 具体编码 | 60 | 60 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 40 | 40 |
Reporting | 报告 | 60 | 60 |
Test Repor | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 50 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 710 | 770 |