软工作业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");
    });
}

总结

通过模块化设计、性能优化、单元测试和异常处理,代码的健壮性、可维护性和性能得到了显著提升。用户在使用时能够获得清晰的错误提示,并通过单元测试确保功能的正确性。单元测试覆盖了所有核心功能,性能分析图展示了关键函数的性能瓶颈,测试覆盖率截图证明了代码的高覆盖率。这些改进显著提升了代码的质量和可维护性。

posted @ 2025-03-07 20:49  洛小泽  阅读(62)  评论(0)    收藏  举报