第一次个人编程作业

https://github.com/Guotao20050521/3123006072/tree/master

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477
这个作业的目标 对某种语言相应的测试工具使用有个初步的理解;熟悉用git管理和上传代码的步骤;熟悉PSP的有关内容

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 15
· estimate · 估计这个任务需要多少时间 330 -
Development 开发 240 300
· analysis · 需求分析 (包括学习新技术) 60 15
· design spec · 生成设计文档 30 15
· design review · 设计复审 15 5
· coding standard · 代码规范 (为目前的开发制定合适的规范) 10 15
· design · 具体设计 30 30
· coding · 具体编码 30 15
· code review · 代码复审 5 5
· test · 测试(自我测试,修改代码,提交修改) 60 210
Reporting 报告 30 25
·test report ·测试报告 10 10
·size measurement ·计算工作量 10 5
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 10
合计 280 340

一、模块接口的设计与实现

本项目用Java语言实现,包含PlagiarismChecker这一个类。类中各方法的功能如下表所示。

方法名 返回值 形参 功能
main void String[] args 主方法。读取参数,执行程序,输出结果
checkPlagiarism double String originalFilePath, String plagiarizedFilePath 计算两个文件的相似度
readFile String String filePath 读取文件内容
preprocessText String String text 文本预处理,去除标点符号和空格,统一格式
calculateSimilarity double String text1, String text2 计算两个文本的相似度
calculateCharBasedSimilarity double String text1, String text2 基于字符的相似度计算
writeResult void double similarity, String answerFilePath 将结果写入答案文件
compareTexts double String text1, String text2 用于单元测试的辅助方法,比较两个字符串的相似度

其中,calculateSimilarity()方法的流程图如下:
image
calculateCharBasedSimilarity()的流程图如下:
image
算法关键&独到之处:用字符交、并集合的大小比值(Jaccard相似度)来衡量重复程度。

二、模块接口部分的性能分析

用JVM分析工具VisualVM来分析应用程序。由于该程序运行速度较快,为保证程序能被VisualVM捕捉,在main()方法的开头加上了两行语句(已在提交时删去):

Scanner sc = new Scanner(System.in);
sc.nextLine();  //测试用,与功能实现无关

这样在程序运行后,需按Enter才会开始执行原有逻辑。性能分析图如下:
截屏2025-09-20 16.24.38
由该图可以看出:程序本身运行速度很快,最消耗时间的反而是为了控制速度而加的等待语句😂
内存分析如下:
截屏2025-09-20 16.25.27
由图可以发现,有个byte数组较占用空间,应是Java库代码所致。HashMap所占空间虽较小,但也值得留意。推测来自上述两个计算相似度的方法。此外还可以看出库代码中使用了反射机制(java.lang.reflect.Method)。
Java存在垃圾回收机制,该部分可视图如下:
image
由此看出程序执行中花了约11ms在垃圾回收中。
系统资源使用情况如下:
image
右上子图中堆大小有明显减小,是因为在VisualVM中点击了"Perform GC"导致。减小后,堆大小与程序实际占用较接近。

三、单元测试展示

1. 测试文本预处理功能

代码如下:

private static void testPreprocessText() {
        System.out.println("开始测试文本预处理功能...");

        // 测试用例1:包含标点符号和空格的文本
        String text1 = "今天是星期天,天气晴,今天晚上我要去看电影。";
        String expected1 = "今天是星期天天气晴今天晚上我要去看电影";
        String result1 = preprocessTextForTest(text1);
        assert result1.equals(expected1) : "测试用例1失败: 期望 '" + expected1 + "', 实际 '" + result1 + "'";
        System.out.println("测试用例1通过");

        // 测试用例2:包含英文和数字的文本
        String text2 = "Hello, World! 123";
        String expected2 = "helloworld123";
        String result2 = preprocessTextForTest(text2);
        assert result2.equals(expected2) : "测试用例2失败: 期望 '" + expected2 + "', 实际 '" + result2 + "'";
        System.out.println("测试用例2通过");

        System.out.println("文本预处理功能测试通过\n");
    }

测试的方法是preprocessText()("ForTest"结尾的方法与被测试程序中的相同)。

2. 测试文本比较功能

private static void testCompareTexts() {
        System.out.println("开始测试文本比较功能...");

        // 测试用例1:完全相同的文本
        String text1 = "今天是星期天天气晴";
        String text2 = "今天是星期天天气晴";
        double similarity1 = PlagiarismChecker.compareTexts(text1, text2);
        assert Math.abs(similarity1 - 1.0) < 0.01 : "测试用例1失败: 期望 1.0, 实际 " + similarity1;
        System.out.println("测试用例1通过:完全相同文本相似度为 " + String.format("%.2f", similarity1));

        // 测试用例2:完全不同的文本
        String text3 = "今天是星期天";
        String text4 = "明天是星期一";
        double similarity2 = PlagiarismChecker.compareTexts(text3, text4);
        assert similarity2 >= 0 : "测试用例2失败: 相似度应该大于等于0, 实际 " + similarity2;
        System.out.println("测试用例2通过:不同文本相似度为 " + String.format("%.2f", similarity2));

        // 测试用例3:部分相同的文本
        String text5 = "今天是星期天天气晴";
        String text6 = "今天是周天天气晴朗";
        double similarity3 = PlagiarismChecker.compareTexts(text5, text6);
        assert similarity3 > 0 && similarity3 < 1 : "测试用例3失败: 相似度应该在0-1之间, 实际 " + similarity3;
        System.out.println("测试用例3通过:部分相同文本相似度为 " + String.format("%.2f", similarity3));

        System.out.println("文本比较功能测试通过\n");
    }

测试的方法为compareTexts()。

3. 测试文件操作功能

private static void testFileOperations() {
        System.out.println("开始测试文件操作功能...");

        try {
            // 创建测试文件
            String originalFile = "original_test.txt";
            String plagiarizedFile = "plagiarized_test.txt";
            String answerFile = "answer_test.txt";

            // 写入测试内容
            writeTestFile(originalFile, "今天是星期天,天气晴,今天晚上我要去看电影。");
            writeTestFile(plagiarizedFile, "今天是周天,天气晴朗,我晚上要去看电影。");

            // 测试查重功能
            double similarity = PlagiarismChecker.checkPlagiarism(originalFile, plagiarizedFile);
            assert similarity >= 0 && similarity <= 1 : "文件操作测试失败: 相似度应该在0-1之间, 实际 " + similarity;

            // 测试结果写入功能
            PlagiarismChecker.writeResult(similarity, answerFile);

            // 验证结果文件内容
            String result = readTestFile(answerFile);
            assert result.matches("\\d+\\.\\d{2}") : "结果文件格式错误: " + result;

            // 清理测试文件
            new File(originalFile).delete();
            new File(plagiarizedFile).delete();
            new File(answerFile).delete();

            System.out.println("文件操作功能测试通过,结果文件内容: " + result + "\n");
        } catch (Exception e) {
            e.printStackTrace();
            assert false : "文件操作测试异常: " + e.getMessage();
        }
    }

测试方法是writeResult()。

4. 测试覆盖率截图

image
测试报告有关内容在Github仓库中的"htmlReport"中。

四、计算模块异常处理

核心思路:

  • 如果两个文本都为空,则相似度为1。
  • 如果其中一个文本为空,则相似度为0。
    在calculateCharBasedSimilarity()方法的最后一行,有语句:
    return (double) intersection.size() / union.size(); //返回交集与并集的比值(Jaccard相似度)
    若union.size()为0则会出现除0异常。为避免该异常,在这行语句前加上:
/*如果并集为空,说明两个集合都为空,相似度为1*/
if (union.isEmpty()) {
  return 1.0;
}

测试样例可设置为两个传入的文件(原文或抄袭版论文)为空的情况。

posted @ 2025-09-20 18:18  Gt3  阅读(37)  评论(0)    收藏  举报