第一次个人编程作业
| 这个作业属于那个课程 | 个人项目 |
|---|---|
| 这个作业要求在哪里 | 作业要求 |
| 这个作业的目标 | 本次个人项目的目标是以“论文查重”为题,完整实践一次规范化的软件开发流程:使用Git进行代码管理并分阶段提交,通过代码质量与性能分析工具优化程序,设计单元测试用例并生成覆盖率报告,最终实现一个能在5秒内稳定输出重复率、具备异常处理能力的查重程序,并通过博客对开发过程进行总结,从而提升算法设计、代码规范、版本控制与测试评估的综合能力。 |
GitHob仓库链接:https://github.com/Akida1122-akd/3224004266
一、PSP 表格(预估 vs 实际)
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 15 | 20 |
| · Estimate | ·估计这个任务需要多长时间 | 15 | 20 |
| Development | 开发 | 120 | 200 |
| · Analysis | ·需求分析(包括学习新技术) | 30 | 45 |
| ·Design Spec | ·生成设计文档 | 20 | 20 |
| ·Design Reciew | ·设计复审 | 10 | 10 |
| ·Coding Standard | ·代码规范 | 10 | 15 |
| ·Design | ·具体设计 | 20 | 30 |
| ·Coding | ·具体编码 | 50 | 80 |
| ·Code Reviw | ·代码复审 | 10 | 10 |
| ·Test | ·测试(自我测试,修改代码,提交修改) | 30 | 60 |
| Reporting | 报告 | 30 | 40 |
| ·Test Report | ·测试报告 | 15 | 20 |
| ·Size Measurement | ·计算功能工作量 | 10 | 10 |
| ·Postmortem &Process lmprovement Plan | ·事后总结,并提出过程改进计划 | 15 | 20 |
| 合计 | 254 | 380 |
二、计算模块接口的设计与实现过程
1. 算法关键
本程序的核心算法使用 Python 内置的 difflib.SequenceMatcher。它基于最长公共子序列(LCS) 来计算两个字符串的相似度,计算公式为 2 * M / T(M 为匹配字符数,T 为总字符数)。相比于余弦相似度,这个算法对中文短文本的查重更加精准,并且完全不需要安装第三方依赖库。
2. 代码组织与函数关系
整个程序共有三个核心函数,它们之间的关系是“流水线”式的:
-
read_text(path: Path) -> str:负责读取文件。内部包含 try...except 异常防护,若文件不存在或编码错误,返回空字符串 ""。
-
plagiarism_rate(original: str, copy: str) -> float:负责查重计算。接收两个字符串,内部使用 SequenceMatcher 计算相似度,返回 0.0 到 1.0 之间的浮点数。
-
main() -> None:主入口。使用 argparse 解析命令行参数(原文、抄袭文、结果文件),依次调用上述两个函数,最后将结果格式化(保留两位小数)写入指定的 ans.txt 中。
3. 独到之处
在调用 SequenceMatcher 时,设置了 autojunk=False 参数。如果不设置,算法会自动忽略掉长文本中高频出现的字符(自动垃圾过滤),这会导致查重结果不准确。禁用该功能后,查重更加严谨。

三、计算模块接口部分的性能改进
1. 改进思路
最初我考虑使用“jieba 分词 + TF-IDF + 余弦相似度”的方案。但分析后发现,这需要引入第三方库,评测环境不一定支持,且计算量大。
后来我改为使用 Python 内置的 difflib.SequenceMatcher,不仅无需安装任何依赖,而且执行速度极快(毫秒级),完全满足评测要求的“5 秒内出结果”。
2. 性能分析
在终端运行 python -m cProfile -s cumtime main.py orig.txt orig_add.txt ans.txt 进行性能分析,结果如下:
-
SequenceMatcher 的 ratio() 函数是耗时最大的模块(通常占总耗时的 90% 以上),但整体耗时仍在 0.001s 级别。
-
由于算法本身已足够快,无需进一步优化,只需保证在极端情况下(如超大文本)不超时即可。

四、计算模块部分单元测试展示
1. 测试思路
在编码完成后,我使用 Python 内置的 unittest 框架设计了 12 个测试用例,采用白盒测试思路,覆盖了以下边界场景:
- 完全相同(相似度应为 1.00)
- 完全不同(相似度应为 0.00)
- 部分抄袭(相似度应在 0.0 到 1.0 之间)
- 原文为空、抄袭文为空、两者都为空
- 纯空格或纯标点符号
- 超长文本(测试性能稳定性)
- 英文大小写区分
- 特殊字符和换行符
- 文件不存在(异常处理测试)
- 读取文件发生异常(异常处理测试)
2. 测试结果
经过本地运行,所有 12 个测试用例全部通过(OK)。




五、计算模块部分异常处理说明
1. 异常设计目标
程序必须保证在评测过程中不发生异常退出。因此,在 read_text 函数中,我设计了 try...except 防护:
def read_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8")
except (FileNotFoundError, UnicodeDecodeError):
return ""
-
当输入文件路径不存在时(FileNotFoundError),返回空字符串。
-
当文件编码不为 utf-8 时(UnicodeDecodeError),返回空字符串。
2. 单元测试例子
在测试用例 test_file_not_found 中,我传入了不存在的文件路径。程序没有崩溃,而是将 ans.txt 写入 0.00。这证明了异常处理逻辑的有效性。

六、事后总结与过程改进
1. 实际耗时统计
结合 PSP 表格,本次作业实际耗时约 380 分钟,远超预估的 245 分钟。主要额外时间花在了调试 Python 环境(uv 环境的输出静默问题)以及配置 Git 远程推送网络异常上。
2. 过程改进计划
-
环境方面:下次开发前应尽早确认 Python 虚拟环境是否能正常工作,避免在后期因环境干扰导致代码无法运行。
-
版本控制方面:熟悉了 Git 的分支管理和远程推送流程。在遇到网络问题时,学会了使用 git push -u origin master:main --force 强制覆盖,确保代码能按时推送到云端。
-
测试方面:掌握了 unittest 和 unittest.mock 的使用方法,能够模拟系统参数(sys.argv)来测试命令行工具,并能编写覆盖边界情况的测试用例,极大地提升了代码的健壮性。

浙公网安备 33010602011771号