软件工程第二次作业

这个作业所属课程 软件工程
这个作业要求在哪 作业需求有说明
这个作业目标 设计一个论文查重的个人项目

github仓库
https://github.com/yiziff/3123004422

Personal Software Process Stages 预估耗时(分钟 实际耗时(分钟)
计划 30 30
估计这个任务需要多少时间 30 30
开发 200 250
需求分析 (包括学习新技术) 30 30
生成设计文档 15 20
设计复审 20 20
· 代码规范 (为目前的开发制定合适的规范) 20
具体设计 120 120
具体编码 50 50
代码复审 30 35
· 测试(自我测试,修改代码,提交修改 30 30
报告 40 70
测试报告 20 20
计算工作量 20 80
事后总结, 并提出过程改进计划 20 25
· 合计 655 680

项目结构设计

chachong4422/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com/ityiz/example/ # 包结构
│ │ │ │ ├── Main.java # 主程序
│ │ │ │ ├── TextsimilarityCalculator.java # 相似度计算模块
│ │ │ │ └── TextUtils.java # 辅助工具模块(可选)
│ │ └── resources/ # 配置文件或资源(当前无)
│ └── test/ # 单元测试目录
│ └── java/
│ └── com/ityiz/example/ # 测试类
└── pom.xml # Maven 配置文件

独到之处(设计与实现的优势)

架构极简,无依赖负担:

仅 2 个核心类,无第三方库依赖(如不依赖 Apache Commons、FastJSON 等),JAR 包体积小(仅 KB 级),可在任何安装 Java 的环境中直接运行,移植性极强,尤其适合轻量化部署。
中文友好,适配实际需求:

两点保障中文处理准确性:

readFile明确使用 UTF-8 编码,规避 Windows 默认 GBK 编码导致的乱码问题;② isChinese精准判断 CJK 统一汉字,确保中文文本在预处理和计算中不被误过滤,解决 “中文查重准确率低” 的常见痛点。

异常处理完备,易于调试:

提前校验参数

checkArguments在流程初期拦截无效参数,避免后续无意义计算;

统一异常捕获:

main方法通过catch (Exception e)捕获所有可能异常(文件不存在、权限不足、编码错误等),并打印错误信息 + 堆栈,开发者可快速定位问题(如 “文件路径错误”“文本编码不兼容”)

类与函数的依赖关系

TextsimilarityCalculator 类(4 个核心函数)

函数名 入参 返回值 核心功能 访问权限
calculateEditDistance String s1(原文)、String s2(抄袭文本) int(最少编辑次数) 动态规划实现编辑距离(Levenshtein Distance) public static
calculateSimilarity String s1、String s2 double(相似度,0-1 区间) 相似度映射:基于编辑距离与文本最大长度计算 public static
readFile String filePath(文件绝对路径) String(文件内容) UTF-8 编码读取文本,避免中文乱码 public static
writeResult double similarity(相似度)、String outputPath(输出路径) void 格式化结果(保留 2 位小数)并写入文件 public static
两个类通过静态方法直接调用关联,无对象实例依赖,耦合度极低,具体协作逻辑如下:

exported_image (2)

改进方向 具体方案
空间复杂度优化 将编辑距离计算的 int[m+1][n+1] 二维 DP 数组,优化为 int[2][n+1] 双行数组(仅保留当前行与上一行)
···
public static int calculateEditDistance(String s1, String s2) {
// 确保 s1 是较短文本,减少列数(进一步优化空间)
if (s1.length() > s2.length()) {
    String temp = s1;
    s1 = s2;
    s2 = temp;
}
int m = s1.length();
int n = s2.length();

// 优化:用双行数组替代二维数组,仅存当前行(curr)与上一行(prev)
int[] prev = new int[n + 1];
int[] curr = new int[n + 1];

// 初始化上一行(空文本到 s2 的编辑距离)
for (int j = 0; j <= n; j++) {
    prev[j] = j;
}

// 填充数组:仅更新当前行与上一行
for (int i = 1; i <= m; i++) {
    curr[0] = i; // 当前行第 0 列(s1 前 i 字符到空文本的距离)
    for (int j = 1; j <= n; j++) {
        if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
            curr[j] = prev[j - 1];
        } else {
            curr[j] = 1 + Math.min(Math.min(curr[j - 1], prev[j]), prev[j - 1]);
        }
    }
    // 交换当前行与上一行,准备下一轮循环
    int[] temp = prev;
    prev = curr;
    curr = temp;
    // 重置当前行(避免下一轮循环污染)
    Arrays.fill(curr, 0);
}
return prev[n]; // 上一行最终值即为编辑距离

}

消耗最大的函数:TextsimilarityCalculator.calculateEditDistance(占总耗时的 62%),但相比改进前(占总耗时 85%)占比显著下降,验证了优化效果。

单元测试代码


@Test
    public void testSimilarityCalculation() {
        // 完全相同文本(DP相似度应为1.0)
        String text1 = "今天天气很好";
        String text2 = "今天天气很好";
        double similarity1 = TextsimilarityCalculator.calculateSimilarity(text1, text2);
        assertEquals("相同文本相似度应为1.0", 1.0, similarity1, 0.001);

        // 部分相似文本(DP相似度应>0.7)
        String text3 = "今天天气不错";
        double similarity2 = TextsimilarityCalculator.calculateSimilarity(text1, text3);
        assertTrue("部分相似文本相似度应较高", similarity2 > 0.5);

        // 完全不同文本(DP相似度应<0.3)
        String text4 = "明天会下雨";
        double similarity3 = TextsimilarityCalculator.calculateSimilarity(text1, text4);
        assertTrue("不同文本相似度应较低", similarity3 < 0.3);

        // 同时验证哈希指纹逻辑(参考用例要求)
        long hash1 = Main.generateDocumentFingerprint(text1);
        long hash2 = Main.generateDocumentFingerprint(text2);
        double hashSimilarity1 = 1.0 - (double)Main.computeDifferenceScore(hash1, hash2)/64;
        assertEquals("相同文本哈希相似度应为1.0", 1.0, hashSimilarity1, 0.001);
    }
参数校验 覆盖 “有效参数”“参数数量不足”“空路径” 三类场景,验证边界条件。
编辑距离计算 覆盖 “完全相同”“部分差异”“一个空文本”“长度不同” 四类场景,确保算法正确性。
相似度计算 设计 “100% 匹配”“50% 匹配”“0% 匹配” 三类用例,验证相似度映射到 [0,1] 区间的准确性。
文本分词 含标点、空白符、中英文混合文本,验证过滤逻辑是否保留有效字符。
文件读取 用 UTF-8 编码的小文件(100 字符内),避免大文件导致测试耗时久。
![](![](image
)
)

计算模块异常处理说明

异常最终在 Main 类的 main 方法中统一捕获,集中输出错误信息,保证程序崩溃时的可调试性

功能测试:验证 Main.extractFileContent 方法是否能正确读取文件内容。

测试中创建了一个临时文件,写入中文内容 "测试内容"(使用 UTF-8 编码),然后调用 extractFileContent 方法提取内容,并通过 assertEquals 检查提取结果是否与预期一致。

你的单元测试方法 testExtractFileContent 是用来测试 Main.extractFileContent 方法的功能和异常处理的。让我们详细分析这个测试的目标以及它所针对的异常场景。

  1. 测试目标

功能测试:验证 Main.extractFileContent 方法是否能正确读取文件内容。

测试中创建了一个临时文件,写入中文内容 "测试内容"(使用 UTF-8 编码),然后调用 extractFileContent 方法提取内容,并通过 assertEquals 检查提取结果是否与预期一致。

异常相关性:虽然这个测试方法本身没有明确使用 @Test(expected = Exception.class) 或其他异常预期标记,但它间接测试了方法在正常情况下的行为。如果 extractFileContent 在处理文件读取时抛出异常(例如文件不存在或权限问题),测试将失败,从而间接验证异常处理的边界。

  1. 测试的异常场景
    Main.extractFileContent 方法调用了 TextsimilarityCalculator.readFile,而 readFile 使用 Files.readAllBytes 读取文件内容。以下是可能涉及的异常场景:

文件不存在异常 (NoSuchFileException)

场景:如果传入的文件路径无效或文件未创建,Files.readAllBytes 会抛出 NoSuchFileException。
测试中的 File.createTempFile 确保了文件存在,因此该异常不会在此测试中触发。但如果测试故意传入不存在的文件路径(例如未创建前调用),会触发此异常。

// 测试文件内容提取
    @Test
    public void testExtractFileContent() throws Exception {
        // 创建临时测试文件
        File tempFile = File.createTempFile("test", ".txt");
        tempFile.deleteOnExit(); // 测试后自动删除
        // 写入测试内容(中文)
        java.nio.file.Files.write(tempFile.toPath(), "测试内容".getBytes("UTF-8"));
        // 验证内容提取
        String content = Main.extractFileContent(tempFile.getAbsolutePath());
        assertEquals("文件内容提取错误", "测试内容", content);
    }
posted @ 2025-09-23 22:43  朱一凡  阅读(8)  评论(0)    收藏  举报