第一次个人编程作业
论文查重
GitHub作业链接:https://github.com/MengXing-OwO/SoftwareEngineering/tree/main/3124004100/论文查重
一、PSP表格(预估与计划)
在开始实现程序之前,我使用PSP表格对各个开发阶段的时间进行了预估,并在完成项目后记录了实际耗时。
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 45 |
| · Estimate | · 估计这个任务需要多少时间 | 30 | 45 |
| Development | 开发 | 400 | 580 |
| · Analysis | · 需求分析 (包括学习新技术) | 60 | 90 |
| · Design Spec | · 生成设计文档 | 30 | 40 |
| · Design Review | · 设计复审 | 20 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 40 |
| · Design | · 具体设计 | 40 | 60 |
| · Coding | · 具体编码 | 120 | 150 |
| · Code Review | · 代码复审 | 30 | 45 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 120 | 180 |
| Reporting | 报告 | 90 | 120 |
| · Test Report | · 测试报告 | 30 | 40 |
| · Size Measurement | · 计算工作量 | 20 | 30 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 60 |
| 合计 | 650 | 900 |
二、计算模块接口的设计与实现过程
1. 代码组织与函数设计
本项目采用 Python 3.12 编写,遵循模块化设计原则,主要分为四个核心函数:
read_file(filepath: str) -> str:负责从绝对路径读取文本内容,处理文件不存在和编码异常。get_word_set(text: str) -> set:核心分词函数。使用jieba对文本进行分词,并过滤标点符号、单字和停用词,返回词汇集合。calculate_similarity(orig_text: str, copy_text: str) -> float:核心计算函数。计算两个词汇集合的 Jaccard 相似度。main():主入口,负责命令行参数解析、调用核心函数、格式化输出并写入答案文件。
2. 算法核心(独到之处)
- Jaccard 相似度:算法不直接比较文本内容,而是将文本分词后转为集合。计算公式为:
交集大小 / 并集大小。这种基于集合的算法能够有效应对文本的增删改。 - 边界处理:代码中考虑了空文件、纯标点、单字过滤等边界情况,确保程序在各种极端输入下不会崩溃。
- 命令行规范:严格按照题目要求,从
sys.argv接收三个绝对路径(原文、抄袭版、答案文件),并以空格分隔。输出结果严格保留两位小数。 - 设计流程图:主程序依次调用
read_file读取两个文本,传入get_word_set进行分词过滤,再由calculate_similarity计算相似度,最后main()负责写入ans.txt文件。
三、计算模块接口部分的性能改进
1. 性能分析(优化前)
为了测试性能瓶颈,我构造了包含大量文本的 orig_big.txt 文件。使用 Python 内置的 cProfile 和 snakeviz 生成了性能分析冰柱图。


分析结论:程序总耗时约 1.04秒,其中 get_word_set 中的 jieba 分词操作耗时 0.67秒,占总耗时的 64% 以上,是程序的性能瓶颈。耗时最大的函数为 jieba.__init__.py 内部的 _cut_DAG 和 get_DAG。
2. 性能改进(优化后)
改进思路:
- 最初尝试使用
jieba.enable_parallel()进行多核并行分词加速,但在 Windows 环境下测试时报出NotImplementedError(底层依赖 Linux 的fork机制,Windows 不支持)。 - 因此,我采取了更兼容的优化方案:将分词参数改为
HMM=False(关闭隐马尔可夫模型新词发现),降低计算复杂度;同时引入停用词表过滤高频无意义词(如“的、了、在”),使得集合大小显著减小,降低了集合运算的内存开销。


优化结果:优化后,程序总耗时降至 1.01秒,分词阶段的耗时明显降低。由于算法变化,重复率计算结果从 0.38 变为 0.43,符合算法预期,性能优化有效。
四、计算模块部分单元测试展示
1. 单元测试代码与思路
本项目使用 pytest 框架编写了 12个单元测试用例,测试文件为 test_main.py,与 main.py 处于同一目录。构造测试数据的思路如下:
- 正常功能:完全相同文本(预期1.0)、完全不同文本(预期0.0)、部分相似文本(验证实际Jaccard值)。
- 边界条件:空文本、一个空一个非空、纯标点符号文本。
- 异常情况:文件不存在、非UTF-8编码文件、命令行参数数量不足。
# 单元测试代码节选
def test_similarity_identical():
text = "今天是星期天,天气晴,今天晚上我要去看电影。"
assert calculate_similarity(text, text) == 1.0
def test_similarity_partial():
text1 = "今天是星期天,天气晴,今天晚上我要去看电影。"
text2 = "今天是周天,天气晴朗,我晚上要去看电影。"
assert calculate_similarity(text1, text2) == 0.43 # 优化后的实际值

2. 测试覆盖率截图

使用了 coverage 工具进行覆盖率统计,整体覆盖率达到了 91%。其中 test_main.py 覆盖率100%,main.py 覆盖率80%(未覆盖部分主要为模块入口 if name == 'main': 和某些未触发的异常分支,测试准确可靠)。
五、计算模块部分异常处理说明
在博客中详细介绍每种异常的设计目标,并选取对应的单元测试样例:
FileNotFoundError(文件不存在异常)- 设计目标:当用户从命令行传入的文件路径无效或文件不存在时,程序不应崩溃,而应捕获异常并友好提示。
- 对应测试样例:
test_read_file_not_found - 错误场景:传入
D:/不存在的文件夹/不存在的文件.txt。程序打印错误信息并sys.exit(1)。
UnicodeDecodeError(编码错误异常)- 设计目标:处理读取非 UTF-8 编码(如 GBK)文件时的解码错误。
- 对应测试样例:
test_read_file_encoding_error - 错误场景:传入一个用 GBK 编码保存的文本文件。程序捕获异常,提示“编码错误:文件编码不是 UTF-8,请检查文件格式”。
SystemExit(命令行参数数量错误)- 设计目标:保证程序严格遵守题目要求的“接收三个绝对路径参数”,参数数量不够时直接终止。
- 对应测试样例:
test_main_missing_arguments - 错误场景:命令行只传入了 2 个参数。程序提示“错误:参数数量不正确!”,并退出,退出码为 1。
六、PSP表格(实际耗时)
在实现完程序之后,我在附录提供的PSP表格中记录下实际花费的时间。
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 45 |
| · Estimate | · 估计这个任务需要多少时间 | 30 | 45 |
| Development | 开发 | 400 | 580 |
| · Analysis | · 需求分析 (包括学习新技术) | 60 | 90 |
| · Design Spec | · 生成设计文档 | 30 | 40 |
| · Design Review | · 设计复审 | 20 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 40 |
| · Design | · 具体设计 | 40 | 60 |
| · Coding | · 具体编码 | 120 | 150 |
| · Code Review | · 代码复审 | 30 | 45 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 120 | 180 |
| Reporting | 报告 | 90 | 120 |
| · Test Report | · 测试报告 | 30 | 40 |
| · Size Measurement | · 计算工作量 | 20 | 30 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 60 |
| 合计 | 650 | 900 |
七、总结与反思
本次软件工程作业让我完整地体验了“需求分析 -> 设计 -> 编码 -> 测试 -> 性能优化 -> 文档撰写”的完整软件开发流程。
在环境配置上踩了不少坑(如 Anaconda 环境与系统环境冲突、PyCharm 静态分析误报等),但也正是这些经历让我学会了独立排查问题、搜索解决方案。
在性能优化阶段,由于 Windows 平台不支持 jieba 的并行分词,我通过查文档找到了 HMM=False 和停用词过滤的替代方案,这让我深刻理解了“具体问题具体分析”的工程思维。
代码质量方面,通过了 PyCharm 的静态分析,消除了所有警告,变量命名规范(小写+下划线),注释完整。算法在 5 秒内能顺利处理大文本,占用内存远低于 2048MB,实现了预期的准确度。

浙公网安备 33010602011771号