第一次个人编程作业

论文查重

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 内置的 cProfilesnakeviz 生成了性能分析冰柱图。

屏幕截图 2026-09-13 154156
屏幕截图 2026-09-13 154257

分析结论:程序总耗时约 1.04秒,其中 get_word_set 中的 jieba 分词操作耗时 0.67秒,占总耗时的 64% 以上,是程序的性能瓶颈。耗时最大的函数为 jieba.__init__.py 内部的 _cut_DAGget_DAG

2. 性能改进(优化后)

改进思路

  • 最初尝试使用 jieba.enable_parallel() 进行多核并行分词加速,但在 Windows 环境下测试时报出 NotImplementedError(底层依赖 Linux 的 fork 机制,Windows 不支持)。
  • 因此,我采取了更兼容的优化方案:将分词参数改为 HMM=False(关闭隐马尔可夫模型新词发现),降低计算复杂度;同时引入停用词表过滤高频无意义词(如“的、了、在”),使得集合大小显著减小,降低了集合运算的内存开销。

屏幕截图 2026-09-13 155513
屏幕截图 2026-09-13 155518

优化结果:优化后,程序总耗时降至 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 # 优化后的实际值

屏幕截图 2026-09-13 151521

2. 测试覆盖率截图

屏幕截图 2026-09-13 154729
使用了 coverage 工具进行覆盖率统计,整体覆盖率达到了 91%。其中 test_main.py 覆盖率100%,main.py 覆盖率80%(未覆盖部分主要为模块入口 if name == 'main': 和某些未触发的异常分支,测试准确可靠)。

五、计算模块部分异常处理说明

在博客中详细介绍每种异常的设计目标,并选取对应的单元测试样例:

  1. FileNotFoundError(文件不存在异常)
    • 设计目标:当用户从命令行传入的文件路径无效或文件不存在时,程序不应崩溃,而应捕获异常并友好提示。
    • 对应测试样例test_read_file_not_found
    • 错误场景:传入 D:/不存在的文件夹/不存在的文件.txt。程序打印错误信息并 sys.exit(1)
  2. UnicodeDecodeError(编码错误异常)
    • 设计目标:处理读取非 UTF-8 编码(如 GBK)文件时的解码错误。
    • 对应测试样例test_read_file_encoding_error
    • 错误场景:传入一个用 GBK 编码保存的文本文件。程序捕获异常,提示“编码错误:文件编码不是 UTF-8,请检查文件格式”。
  3. 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,实现了预期的准确度。

posted @ 2026-09-13 16:40  夢醒  阅读(11)  评论(0)    收藏  举报