第一次个人项目
| 软件工程 | 软件工程 |
|---|---|
| 这个作业要求在哪里 | 作业要求 |
| 这个作业的目标 | 使用 Python 实现基于 SimHash 算法的文本查重程序,读取待比对的两份文本,计算文本相似度并输出结果;同时完成代码规范检查、单元测试、性能分析,用 Git 在 GitHub 管理项目代码。 |
Part 1 github链接
https://github.com/3214153741-sudo/paper-checking
Part 2 PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) |
|---|---|---|
| Planning | 计划 | 20 |
| · Estimate | ·估计这个任务需要多少时间 | 20 |
| Development | 开发 | 240 |
| · Analysis | ·需求分析 (包括学习新技术) | 40 |
| · Design Spec | ·生成设计文档 | 20 |
| · Design Review | ·设计复审 | 15 |
| · Coding Standard | ·代码规范 (为目前的开发制定合适的规范) | 15 |
| · Design | ·具体设计 | 30 |
| · Coding | ·具体编码 | 80 |
| · Code Review | ·代码复审 | 20 |
| · Test | ·测试(自我测试,修改代码,提交修改) | 20 |
| Reporting | 报告 | 60 |
| · Test Repor | ·测试报告 | 20 |
| · Size Measurement | ·计算工作量 | 15 |
| · Postmortem & Process Improvement Plan | ·事后总结, 并提出过程改进计划 | 25 |
| ·合计 | 320 |
Part 3 计算模块接口的设计与实现过程
本次项目基于 SimHash 算法实现中文文本查重工具,整体代码采用函数式组织,没有定义类,由多个独立函数分工完成文本处理、指纹计算、相似度比对功能,函数之间依靠参数传递数据。
代码结构与函数关系
- preprocess_text():文本预处理函数。负责读取文本,去除多余换行、空格、标点符号,调用分词库对中文句子分词,输出清洗后的词语列表;是整个程序的前置模块,为 SimHash 计算提供干净的分词结果。
- calculate_hash():计算单个词语的哈希值,将词语映射为固定长度二进制串。
- simhash():核心 SimHash 指纹生成函数,遍历分词列表,累加各位权重,生成文本 64 位 SimHash 指纹。调用calculate_hash()得到每个词哈希,是本项目核心计算模块。
- hamming_distance():计算两段文本指纹之间的汉明距离。
- calc_similarity():根据汉明距离换算得到文本相似度,保留两位小数。
- main():入口函数,接收命令行传入的三个文件路径参数,依次调用预处理、simhash、汉明距离、相似度计算函数,最后将结果写入输出文件。
调用关系:main() → preprocess_text() → simhash() → calculate_hash();simhash得到两个指纹后,调用hamming_distance(),再传入calc_similarity()得到最终相似度。
独到之处有
- 针对中文场景做文本清洗,过滤无效符号,减少无关字符对指纹的干扰,提升查重准确度;
- 模块化拆分,每个函数职责单一,方便单独单元测试,便于后续维护和性能优化;
- 加入文件读取异常捕获,程序鲁棒性更强,遇到空文件、不存在文件不会直接崩溃。
Part 4 计算模块接口部分的性能改进
在性能分析与模块优化阶段,预估耗时 30 分钟,实际耗时约 35 分钟。主要花费在性能采样、定位热点函数、代码重构与反复对比测试。
改进思路是使用 Python 内置cProfile工具对完整程序进行性能剖析,采集全流程函数调用耗时,识别出程序中simhash () 指纹生成函数为耗时占比最高的热点函数。原版本在循环内重复执行分词与哈希计算,存在不必要的重复运算。

本次使用 cProfile 生成性能分析统计图表,图中可以直观看到各函数累计执行时间:
消耗最大函数为 simhash(),该函数占用了绝大部分 CPU 执行时间;其次是文本预处理preprocess_text()。其余辅助函数(汉明距离、相似度换算)耗时占比很低,不是性能瓶颈。
Part 5 单元测试与测试覆盖率
针对项目核心功能编写单元测试,覆盖文本读取、文本预处理、相似度计算、文件异常等多个分支场景。一共设计 9 个测试用例:包含正常文本比对、空文本输入、不存在文件路径等边界用例,验证各个函数在正常输入与异常输入下能否得到预期结果。
我分别在git bash里面用这两条命令做了测试
使用pytest框架执行全部测试用例,运行后所有 9 个用例全部通过,说明各模块逻辑符合预期:pytest test_main.py -v

配套使用pytest-cov统计代码覆盖率:pytest test_main.py --cov=main

Part 6 异常处理
为了防止程序在文件读写模块期间出错,所以设计了三种提前纠错的检测
1.文件不存在异常
当输入的原文路径、比对文本路径不存在时捕获该异常,打印提示信息 “程序异常:文件不存在” 并安全退出。

-
文件 IO 读写异常
读取文件、写入结果文件时捕获 IO 相关异常,区分读取失败、写入失败两类提示,方便排查权限、路径等问题。
![image]()
-
通用兜底异常
增加通用 Exception 捕获,捕获其余未知运行错误,避免原始堆栈错误直接展示给使用者,提升程序健壮性。
单元测试中专门设计了对应异常场景的测试用例,验证异常捕获分支可以正常触发,异常处理逻辑有效。
![image]()
Part 7 开发实际耗时
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) | |
|---|---|---|---|---|
| Planning | 计划 | 20 | 25 | |
| · Estimate | ·估计这个任务需要多少时间 | 20 | 30 | |
| Development | 开发 | 240 | 300 | |
| · Analysis | ·需求分析 (包括学习新技术) | 40 | 60 | |
| · Design Spec | ·生成设计文档 | 20 | 15 | |
| · Design Review | ·设计复审 | 15 | 10 | |
| · Coding Standard | ·代码规范 (为目前的开发制定合适的规范) | 15 | 15 | |
| · Design | ·具体设计 | 30 | 15 | |
| · Coding | ·具体编码 | 80 | 120 | |
| · Code Review | ·代码复审 | 20 | ||
| · Test | ·测试(自我测试,修改代码,提交修改) | 20 | 30 | |
| Reporting | 报告 | 60 | 60 | |
| · Test Repor | ·测试报告 | 20 | 20 | |
| · Size Measurement | ·计算工作量 | 15 | 10 | |
| · Postmortem & Process Improvement Plan | ·事后总结, 并提出过程改进计划 | 25 | 20 | |
| ·合计 | 320 | 750 |
Part 8 迭代提交记录
项目使用 Git 进行版本管理,代码托管于 GitHub,各次提交迭代如下:
1.初始化项目,学号目录新建main.py与requirements.txt:创建项目基础文件,完成仓库初始化。
2.第一次代码测试:对核心代码进行初步功能测试,验证基础逻辑。
3. 新增注释:为源码添加注释,提升代码可读性。
4.修改为调用txt的方式,之前是写死的文本:改造输入逻辑,从直接写死文本改为读取外部 txt 文件。
5.新增十组用例:扩充单元测试,增加 10 组测试用例覆盖更多场景。
6.新增测试代码覆盖率,和添加gitignore:增加.gitignore 文件,引入工具统计单元测试覆盖率。
7.添加 README.md项目说明文档:编写项目介绍 README,完善仓库主页说明。
8.添加requirements.txt,记录项目依赖包:完善依赖清单,记录项目需要安装的库。
9.代码测试完成:全部功能与测试验证完毕,项目开发收尾。

Part 9 总结
本次项目实践完成了从需求分析、代码开发、迭代完善到测试验证的完整流程。项目中掌握了Git版本控制工具的使用,学会将本地仓库关联GitHub远程仓库,通过多次commit记录代码变更并推送到远端保存版本。开发阶段依据需求完成基础代码实现,再逐步补充功能、增加异常处理逻辑,最后编写单元测试,对正常与异常场景开展验证。本次实践完整展示了增量开发的工作模式,熟悉了软件工程基础开发流程。



浙公网安备 33010602011771号