第一次个人编程作业
作业信息
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS |
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS/homework/15693 |
| 这个作业的目标 | 完成一个论文查重程序,实践需求分析、设计、编码、测试、性能分析与代码质量检查等软件开发流程 |
第一次个人编程作业:论文查重
一、GitHub链接
- GitHub仓库地址:https://github.com/Alan900447/PaperCheck
- Release版本:https://github.com/Alan900447/PaperCheck/releases/tag/v1.0
二、PSP表格
在开始实现程序之前,我估计将在程序的各个模块的开发上耗费的时间如下:
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 25 |
| · Estimate | · 估计这个任务需要多少时间 | 20 | 25 |
| Development | 开发 | 180 | 200 |
| · Analysis | · 需求分析 (包括学习新技术) | 30 | 40 |
| · Design Spec | · 生成设计文档 | 20 | 15 |
| · Design Review | · 设计复审 | 10 | 10 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
| · Design | · 具体设计 | 20 | 20 |
| · Coding | · 具体编码 | 60 | 80 |
| · Code Review | · 代码复审 | 10 | 15 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 30 | 30 |
| Reporting | 报告 | 30 | 30 |
| · Test Report | · 测试报告 | 10 | 10 |
| · Size Measurement | · 计算工作量 | 10 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 10 |
| 合计 | 230 | 255 |
三、计算模块接口的设计与实现过程
3.1 代码组织
本项目采用 Python 实现,主要包含以下文件和类:
main.py: 主入口,负责解析命令行参数,调用核心算法,输出结果。test_main.py: 单元测试文件。
3.2 算法关键
本程序使用 difflib 库中的 SequenceMatcher 算法来计算两段文本的相似度。
- 核心思路:SequenceMatcher 通过计算两个序列的最长连续匹配子序列,进而得出相似度比值(ratio)。
- 独到之处:相对于传统的编辑距离算法,SequenceMatcher 对长文本的处理效率更高,且能自动忽略一些不必要的标点干扰。
四、计算模块接口部分的性能改进
4.1 性能分析
使用 cProfile 对程序进行性能分析,发现主要耗时在 SequenceMatcher 的匹配计算上。
4.2 改进思路与结果
- 改进思路:在处理大文本时,可以先进行文本预处理(去除换行符和多余空格),减少输入序列的长度。
- 改进结果:经过预处理后,长文本的对比时间显著缩短,内存占用也有所降低。
性能分析图:

五、计算模块部分单元测试展示
5.1 测试代码
import unittest
from main import calculate_similarity
class TestSimilarity(unittest.TestCase):
# 测试用例1:两段文本完全相同
def test_identical(self):
self.assertEqual(calculate_similarity("今天天气晴", "今天天气晴"), 1.00)
# 测试用例2:两段文本完全不同
def test_different(self):
self.assertEqual(calculate_similarity("ABC", "XYZ"), 0.00)
# 测试用例3:部分相似
def test_partial(self):
self.assertTrue(0 < calculate_similarity("今天是星期天", "今天是周天") < 1)
# 测试用例4:空字符串 vs 空字符串
def test_empty_both(self):
self.assertEqual(calculate_similarity("", ""), 1.00)
# 测试用例5:一段空,一段非空
def test_one_empty(self):
self.assertEqual(calculate_similarity("ABC", ""), 0.00)
# 测试用例6:标点符号差异
def test_punctuation(self):
self.assertTrue(0 < calculate_similarity("你好,世界!", "你好世界") < 1)
# 测试用例7:包含换行符
def test_newline(self):
self.assertEqual(calculate_similarity("Hello\nWorld", "Hello\nWorld"), 1.00)
# 测试用例8:长文本
def test_long_text(self):
text1 = "这是一个非常长的测试文本,用于检验算法在长文本下的表现。" * 10
text2 = "这是一个非常长的测试文本,用于检验算法在长文本下的表现。" * 10
self.assertEqual(calculate_similarity(text1, text2), 1.00)
# 测试用例9:单字符相同
def test_single_char(self):
self.assertEqual(calculate_similarity("A", "A"), 1.00)
# 测试用例10:单字符不同
def test_single_char_diff(self):
self.assertEqual(calculate_similarity("A", "B"), 0.00)
if __name__ == '__main__':
unittest.main()
5.2 测试覆盖率
使用 coverage 工具运行测试,结果如下:

5.3 构造测试数据的思路
- 边界测试:空字符串、单字符、超长文本。
- 功能测试:完全相同、完全不同、部分相同(增删改)。
- 异常测试:文件不存在、文件编码错误。
六、计算模块部分异常处理说明
本程序在设计中充分考虑了各种异常情况,以保证程序的健壮性。以下列举两种典型的异常处理及其对应的单元测试场景:
6.1 文件不存在异常(FileNotFoundError)
- 设计目标:当用户输入的文件路径不存在时,程序不应直接崩溃,而应给出友好的提示,并优雅退出。
- 错误场景:用户在命令行传入了一个不存在的文件路径,如
python main.py no_exist.txt plag.txt ans.txt。 - 单元测试样例:
def test_file_not_found(self):
# 模拟调用读取不存在文件的情况
with self.assertRaises(SystemExit):
read_file("no_exist.txt")
6.2 文件编码错误异常(UnicodeDecodeError)
- 设计目标:当读取的文件不是 UTF-8 编码(例如 GBK 编码)时,程序应捕获异常并提示用户转换编码。
- 错误场景:用户提供了一个使用 GBK 编码保存的文本文件,程序默认以 UTF-8 读取,触发解码错误。
- 单元测试样例:
def test_encoding_error(self):
# 模拟读取非 UTF-8 编码文件的场景
with self.assertRaises(UnicodeDecodeError):
read_file("gbk_encoded.txt")
七、总结与改进
通过本次个人编程作业,我完整地实践了从环境搭建、代码编写到单元测试、版本控制的软件工程流程,收获颇丰。
主要收获:
- 掌握了 Git 版本控制:学会了
git add、git commit、git push等基础操作,理解了代码"存档"与回溯的意义。 - 熟悉了单元测试:通过
unittest和coverage工具,学会了编写边界测试用例并查看代码覆盖率,明白了程序健壮性的重要性。 - 理解了性能分析:使用
cProfile工具定位了性能瓶颈,认识到算法选择对程序效率的影响。
不足与改进:
- PSP 时间预估不够准确,实际耗时比预估要多,今后需要更理性地评估任务时间。
- 目前的查重算法对超长文本的处理效率有限,未来可以尝试使用 SimHash 等更高效的算法进行优化。
- 测试用例主要覆盖了常规边界情况,对异常路径的测试还不够充分,今后应加强异常处理的测试。
总的来说,本次作业让我从"只会写代码"向"会做软件工程"迈出了重要的一步,今后会继续在实践中提升自己的工程能力。
八、附录:PSP 实际耗时记录表
在实现完程序之后,我在下述 PSP 表格中记录了各个模块实际花费的时间:
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 25 |
| · Estimate | · 估计这个任务需要多少时间 | 20 | 25 |
| Development | 开发 | 180 | 200 |
| · Analysis | · 需求分析(包括学习新技术) | 30 | 40 |
| · Design Spec | · 生成设计文档 | 20 | 15 |
| · Design Review | · 设计复审 | 10 | 10 |
| · Coding Standard | · 代码规范(为目前的开发制定合适的规范) | 10 | 10 |
| · Design | · 具体设计 | 20 | 20 |
| · Coding | · 具体编码 | 60 | 80 |
| · Code Review | · 代码复审 | 10 | 15 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 30 | 30 |
| Reporting | 报告 | 30 | 30 |
| · Test Report | · 测试报告 | 10 | 10 |
| · Size Measurement | · 计算工作量 | 10 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 10 | 10 |
| 合计 | 230 | 255 |
浙公网安备 33010602011771号