第一次个人编程作业
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 用java实现论文查重的功能,学会PSP表格的使用 |
Github链接:github地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 45 |
Estimate | 估计这个任务需要多少时间 | 15 | 10 |
Development | 开发 | 600 | 590 |
Analysis | 需求分析 (包括学习新技术) | 120 | 180 |
Design Spec | 生成设计文档 | 40 | 60 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
Design | 具体设计 | 20 | 15 |
Coding | 具体编码 | 180 | 190 |
Code Review | 代码复审 | 20 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 80 |
Reporting | 报告 | 50 | 60 |
Test Repor | 测试报告 | 40 | 50 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 15 | 20 |
合计 | 1220 | 1360 |
模块接口的设计与实现过程
实现关键:
主要通过读取文件内容,生成SimHash值,再利用Hamming距离计算两个文本的相似度。项目的独到之处在于使用了中文分词技术和SimHash加权算法,使其能够快速处理大规模文本数据。
模块设计:
IOUtils
: 负责文件操作,包括读取文件、写入文件、清空文件和获取文件夹中的所有文件路径。
SimHash
: 负责计算SimHash值,包括计算MD5哈希值和从文本生成SimHash值。
HammingUtils
: 负责计算Hamming距离和相似度。
Main
: 负责协调其他模块,执行主要的流程,包括读取文件、计算SimHash、计算相似度、输出结果。
函数设计:
IOUtils类
readTxt(String txtPath)
: 读取文件内容并返回字符串。
writeTxt(String content, String txtPath)
: 将字符串内容写入文件。
writeTxt(double value, String txtPath)
: 将double类型的内容写入文件。
clearFile(String txtPath)
: 清空文件内容。
getFilePaths(String folderPath)
: 获取文件夹中所有文件的路径。
SimHash类
getHash(String str)
: 计算字符串的MD5哈希值并返回二进制字符串。
getSimHash(String str)
: 计算字符串的SimHash值。
HammingUtils类
getHammingDistance(String simHash1, String simHash2)
: 计算两个SimHash值的Hamming距离。
getSimilarity(int distance)
: 通过Hamming距离计算相似度。
Main类
main(String[] args)
: 主函数,执行整体流程,包括读取主文件和比较文件、计算SimHash值和相似度、输出结果。
模块之间的关系:
Main 类:
调用 IOUtils 类的方法来读取文件内容和写入结果。
使用 SimHash 类的方法计算SimHash值。
调用 HammingUtils 类的方法计算Hamming距离和相似度。
SimHash 类:
依赖于getHash
方法来获取单词的MD5哈希值。
在 getSimHash 方法中,调用getHash
生成特征向量。
HammingUtils 类:
计算两个SimHash值的Hamming距离,并通过这个距离计算相似度。
关键函数流程图:
模块接口部分的性能改进
在改进计算模块性能上花了大约20分钟,将IOUtils中的文件读取改为InputStreamReader
,确保在读取包含不同编码的大文件时效率更高等
性能分析图:由JProfiler的性能分析工具自动生成
模块部分单元测试展示
SimHash 类的单元测试
@Test
public void testGetSimHash() {
String content = "Hello, World!";
String simHash = SimHash.getSimHash(content);
String expectedSimHash = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; // Expected value should be calculated based on the method
assertEquals(expectedSimHash, simHash);
}
使用固定的字符串"Hello, World!"计算SimHash值,并与预期的SimHash值进行比对。预期值需要根据实际SimHash算法计算得到
HammingUtils 类的单元测试
@Test
public void testGetHammingDistance() {
String simHash1 = "11010101010101010101010101010101";
String simHash2 = "11010101010101010101010101010001";
int distance = HammingUtils.getHammingDistance(simHash1, simHash2);
assertEquals(1, distance);
}
测试SimHash值:使用已知的SimHash值对来计算Hamming距离,确保计算结果正确。这里选择了两个仅在最后一位不同的SimHash值来计算距离
单元测试得到的测试覆盖率截图:
模块部分异常处理说明
文件读取异常:
点击查看代码
@Test
public void testReadTxt_FileNotFound() {
// 测试读取一个不存在的文件
assertThrows(IOException.class, () -> {
IOUtils.readTxt("nonexistent_file.txt");
});
}
当文件的写入权限不足:
点击查看代码
@Test
public void testWriteTxt_NoWritePermission() {
// 测试写入一个不可写的文件
assertThrows(IOException.class, () -> {
IOUtils.writeTxt("Some content", "/path/to/protected_file.txt");
});
}
处理哈希算法错误:
点击查看代码
@Test
public void testGetHash_InvalidAlgorithm() {
// 模拟一个无效的哈希算法场景
// 这通常需要通过Mock或特殊配置模拟,但在标准环境中MD5始终可用
assertThrows(RuntimeException.class, () -> {
SimHash.getHash("test");
});
}
处理哈希长度不一致:
点击查看代码
@Test
public void testGetHammingDistance_LengthMismatch() {
// 测试长度不匹配的海明距离计算
assertThrows(IllegalArgumentException.class, () -> {
HammingUtils.getHammingDistance("1101", "11010"); // 不同长度
});
}
主文件路径无效:
点击查看代码
@Test
public void testMain_InvalidFilePath() {
// 模拟主文件或比较文件路径无效的场景
assertThrows(IOException.class, () -> {
// 假设主函数中的文件路径无效
Main.main(new String[]{});
});
}