第一次个人编程作业
GitHub仓库链接:https://github.com/onychen/gdut-software-engineering-2026
第一次个人编程作业:论文查重
姓名:陈锦清 学号:3124004049 班级:计科5班
开发时我用了 AI 辅助整理代码和文档。下面的测试和计时数据是我在本机运行得到的;PSP 用时按阶段回忆估算。
题目要求
程序从命令行接收三个绝对路径:原文、待检测文本和答案文件。它读取前两个文件,算出待检测文本相对原文的重复率,再把保留两位小数的结果写入答案文件。Python 入口是 main.py,运行不需要安装第三方库。
程序结构和算法
代码按职责拆成几个函数:main 处理命令行参数,run 负责串起读文件、计算和写答案;read_text_file 和 write_rate_file 处理文件;normalize_text 负责文本规范化;similarity_rate 计算重复率;_gram_codes 生成用于比较的字符片段。
算法先统一全角字符和英文大小写,再忽略空格、标点和符号。随后把文本切成相邻的两个字符作为片段;短文本则按单个字符处理。程序统计原文中每种片段出现的次数,再从待检测文本逐个查找并消耗对应计数,避免候选文本反复使用同一段原文内容。计算公式是:
重复率 = 待检测文本中匹配到的片段数 / 待检测文本的片段总数
因此这是一个有方向的字符片段重合率,不是语义查重。词句换序、同义改写等情况可能识别不出来;清理规则和片段长度也会影响结果。
性能分析
如果把候选文本中的每个片段都逐一和原文比较,长文本会做很多重复工作,复杂度接近 O(N×M)。现在的做法是先为原文建立计数表,再扫描候选文本,时间复杂度为 O(N+M),空间复杂度为 O(U),其中 U 是原文中不同片段的数量。
我用 Python 自带的 cProfile 分析了合成文本:原文 380,000 字符、候选文本 403,000 字符,剖析耗时 0.902 秒。热点是二元组生成器(main.py 第28行),自身耗时 0.273 秒;similarity_rate 累计耗时 0.902 秒。
在 2,400 字符的样例上,计数表算法运行五次的中位数是 0.001588 秒;两两扫描原型的中位数是 0.24699 秒,前者约快 155.1 倍。具体时间会受运行环境影响。


单元测试和代码检查
测试代码在 tests/test_main.py,一共 23 个用例,包含这些情况:
| 测试内容 | 检查结果 |
|---|---|
| 文本相同、截取原文片段 | 重复率为 1.00 |
| 添加不相关内容、完全无关内容 | 检查比例变化和 0.00 边界 |
| 空原文、空候选、单字符文本 | 检查空值和短文本处理 |
| 标点、空白、全半角和大小写 | 检查规范化结果 |
| 候选重复片段超过原文次数 | 确认不会重复消耗原文片段 |
| UTF-8 BOM、GB18030 | 检查文件编码读取 |
| 缺少文件、无效编码、答案路径不可写 | 检查异常提示和退出状态 |
| 命令行参数数量不正确 | 检查参数错误处理 |
运行 python -m unittest discover -s tests -v 后,23 个用例全部通过。Coverage 7.16.0 报告的总覆盖率为 99%,其中语句覆盖率 99%、分支覆盖率 93%;main.py 的语句和分支覆盖率都是 100%。

代码质量检查使用 Ruff 0.16.7,结果是 All checks passed!。
异常处理
遇到输入文件不存在或不可读时,程序会指出文件路径并返回非零状态;文本既不是 UTF-8 也不是 GB18030 时会报解码错误;答案文件无法写入时会指出输出路径。参数数量不正确由 argparse 提示用法。原文或候选文本为空时,程序返回 0.00,不会因除数为零而异常退出。
对应的测试包括 test_missing_file_raises_clear_error、test_invalid_text_encoding_raises_clear_error、test_unwritable_answer_path_raises_clear_error 和 test_cli_requires_three_paths。这些用例分别检查文件缺失、编码错误、输出失败和参数错误。
运行示例
python main.py "C:\tests\orig.txt" "C:\tests\orig_add.txt" "C:\tests\ans.txt"
对题目说明中的示例文本,答案文件内容为 0.62。输出是 0 到 1 之间保留两位小数的比例。
PSP
PSP 预估总计 230 分钟,实际投入回忆估算为 43 分钟:需求 5 分钟、设计 7 分钟、实现和复核 7 分钟、测试 8 分钟、覆盖率与性能测量 8 分钟、报告整理 8 分钟。
总结
这次作业里,重复率的算法定义是比较关键的部分。标点怎么处理、重复片段是否能重复计数、分母取哪一边,都会改变结果。当前程序用字符二元组做局部匹配,速度比较快,但不能识别语义改写。以后可以再试不同片段长度,看看准确率和运行时间怎么变化。
浙公网安备 33010602011771号