软工作业2:个人项目-论文查重
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023 |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023/homework/13324 |
| 这个作业的目标 | <训练个人项目软件开发能力,学会使用性能测试工具和单元测试优化程序> |
GitHub仓库链接
https://github.com/LiYongSheng6/3123004148.git
PSP2.1表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 10 | 15 |
| Estimate | 估计这个任务需要多少时间 | 60 | 100 |
| Development | 开发 | 180 | 220 |
| Analysis | 需求分析 (包括学习新技术) | 30 | 40 |
| Design Spec | 生成技术文档 | 30 | 30 |
| Design Review | 设计复审 | 20 | 20 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
| Design | 具体设计 | 30 | 40 |
| Coding | 具体编码 | 150 | 250 |
| Code Review | 代码复审 | 30 | 30 |
| Test | 测试(自我测试,修改代码,提交修改) | 20 | 30 |
| Reporting | 报告 | 60 | 100 |
| Test Repor | 测试报告 | 20 | 10 |
| Size Measurement | 计算工作量 | 60 | 45 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 40 | 30 |
| 合计 | 750 | 960 |
一.计算模块接口的设计与实现过程
代码组织与设计
1.文件读取模块:
- 负责读取原文和抄袭版文件内容。
- 使用BufferedReader和CharsetDecoder确保文件编码为UTF-8。
- 关键函数:readFileWithEncodingCheck。
2.文件写入模块:
- 将计算结果写入答案文件。
- 关键函数:writeFileWithBuffer。
3.文本预处理模块:
- 去除标点符号和非中文字符,保留纯中文文本。
- 关键函数:processText。
4.n-gram生成模块:
- 将文本分割为连续的n个字符片段(n-gram)。
- 支持并行处理以提高性能。
- 关键函数:generateNGramsParallel。
5.词频统计模块:
- 统计n-gram的出现频率,生成词频映射表。
- 使用ConcurrentHashMap支持并行统计。
- 关键函数:getFrequencyMapParallel。
6.余弦相似度计算模块:
- 基于n-gram词频向量计算余弦相似度。
- 关键函数:computeCosineSimilarity。
类与函数关系
- main函数调用readFileWithEncodingCheck读取文件。
- main函数调用processText预处理文本。
- main函数调用generateNGramsParallel生成n-gram。
- main函数调用getFrequencyMapParallel统计词频。
- main函数调用computeCosineSimilarity计算相似度。
- main函数调用writeFileWithBuffer写入结果。
关键算法
- n-gram生成:将文本分割为连续的n个字符片段,捕捉局部特征。
- 余弦相似度计算:基于n-gram词频向量计算相似度,公式为:

独到之处
- 并行处理:使用并行流(parallelStream)加速n-gram生成和词频统计。
- 编码检查:显式检查文件编码格式,确保输入文件为UTF-8。
- 异常处理:提供详细的错误提示,增强代码健壮性。
二. 计算模块接口部分的性能改进
改进思路
1.并行处理:
- 使用parallelStream并行生成n-gram和统计词频,充分利用多核CPU性能。
- 改进前:单线程处理,耗时较长。
- 改进后:并行处理,性能提升显著。
2.高效数据结构:
- 使用ConcurrentHashMap存储词频,支持并发操作。
- 改进前:使用HashMap,线程不安全。
- 改进后:使用ConcurrentHashMap,线程安全且性能更高。
3.减少IO开销:
- 使用缓冲流(BufferedReader和BufferedWriter)减少IO操作次数。
- 改进前:直接读写文件,IO开销较大。
- 改进后:缓冲流显著减少IO时间。
性能分析
使用JProfiler进行性能分析,结果如下:
- 消耗最大的函数:computeCosineSimilarity,占总运行时间的40%。
- 并行处理效果:n-gram生成和词频统计时间减少约60%。
性能分析图
三. 计算模块部分单元测试展示
单元测试代码
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class PlagiarismCheckerTest {
@Test
void testProcessText() {
String text = "今天是星期天,天气晴,今天晚上我要去看电影。";
String expected = "今天是星期天天气晴今天晚上我要去看电影";
assertEquals(expected, PlagiarismCheckerWithEncodingCheck.processText(text));
}
@Test
void testGenerateNGramsParallel() {
String text = "今天是星期天";
List<String> expected = Arrays.asList("今天是", "天是星", "是星期", "星期天");
assertEquals(expected, PlagiarismCheckerWithEncodingCheck.generateNGramsParallel(text, 3));
}
@Test
void testComputeCosineSimilarity() {
Map<String, Integer> map1 = new HashMap<>();
map1.put("今天是", 1);
map1.put("星期天", 1);
Map<String, Integer> map2 = new HashMap<>();
map2.put("今天是", 1);
map2.put("星期天", 1);
double expected = 1.0;
assertEquals(expected, PlagiarismCheckerWithEncodingCheck.computeCosineSimilarity(map1, map2), 0.01);
}
}
测试数据构造思路
1.testProcessText:
- 输入包含标点符号和非中文字符的文本。
- 验证预处理后是否只保留中文字符。
2.testGenerateNGramsParallel:
- 输入一段短文本。
- 验证生成的n-gram是否正确。
3.testComputeCosineSimilarity:
- 输入两个相同的词频映射表。
- 验证余弦相似度是否为1.0。
测试覆盖率截图

四. 计算模块部分异常处理说明
异常类型及设计目标
1.FileNotFoundException:
- 错误场景:用户提供的文件路径不存在。
- 设计目标:捕获文件路径错误或文件不存在的情况。
- 测试用例:
@Test
void testFileNotFound() {
assertThrows(FileNotFoundException.class, () -> {
PlagiarismCheckerWithEncodingCheck.readFileWithEncodingCheck("non_existent.txt");
});
}
2.UnsupportedEncodingException:
- 错误场景:用户提供的文件编码格式不是UTF-8。
- 设计目标:捕获文件编码非UTF-8的情况。
- 测试用例:
@Test
void testUnsupportedEncoding() {
assertThrows(UnsupportedEncodingException.class, () -> {
PlagiarismCheckerWithEncodingCheck.readFileWithEncodingCheck("non_utf8.txt");
});
}
3.IOException:
- 错误场景:尝试写入受保护的文件。
- 设计目标:捕获其他IO相关异常,如文件权限不足或文件损坏。
- 测试用例:
@Test
void testIOException() {
assertThrows(IOException.class, () -> {
PlagiarismCheckerWithEncodingCheck.writeFileWithBuffer("/root/protected_file.txt", "test");
});
}
浙公网安备 33010602011771号