个人项目1_论文查重
第一次个人编程作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477 |
这个作业的目标 | <编写项目实现论文查重,并进行异常测试和性能分析> |
作业链接:https://github.com/Prinserrrr/3223004820
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 15 |
· Estimate | · 估计任务所需时间 | 15 | 15 |
Development | 开发 | 410 | 440 |
· Analysis | · 需求分析(含学习新技术) | 70 | 70 |
· Design Spec | · 生成设计文档 | 30 | 40 |
· Design Review | · 设计复审 | 20 | 15 |
· Coding Standard | · 代码规范制定 | 10 | 10 |
· Design | · 具体设计 | 60 | 65 |
· Coding | · 具体编码 | 150 | 160 |
· Code Review | · 代码复审 | 20 | 25 |
· Test | · 测试(自我测试+修改提交) | 50 | 55 |
Reporting | 报告 | 200 | 215 |
· Test Report | · 测试报告 | 90 | 90 |
· Size Measurement | · 计算工作量 | 10 | 5 |
· Postmortem & Process Improvement Plan | · 事后总结与改进计划 | 100 | 120 |
· 合计 | 625 | 670 |
一、模块接口设计
-
main.py:顶层模块,调度下层模块实现功能
- read_file(file_path):读取文件并判断编码和是否为空
- preprocess_text(text):文本预处理(去除空白字符、分词)
- calculate_similarity(text1, text2):计算文本相似度
- write_result(file_path, similarity):将结果写入文件
-
test.py:测试函数,测试模块功能
-
test_coding.py:测试函数,测试编码处理
-
create_encoded_files.py:创建不同编码的文件
流程图如下:
算法解析
关键之处:
- 文本向量化:通过TF-ID(词频 - 逆文档频率)算法,将论文文本转换为可计算的高维数值向量。
- 词频(TF):突出文本中高频出现的核心词汇(如专业术语)
- 逆文档频率(IDF):降低在所有文本中均高频出现的通用词(如 “的”“研究”)的权重,避免虚词干扰查重结果
- 相似度计算:通过余弦相似度度量两个TF-IDF向量的夹角,值越接近1表示文本越相似,重复率越高)。
独到之处:
- 灵活适应文件编码:通过chardet自动检测文件编码,文件输入前无需预先知晓文件编码,解决中文文件常见的编码混乱问题;
- 特殊字符兼容:对文本中的标点、符号、emoji等特殊字符不做强制过滤,保留其在向量中的权重,使查重判断更全面。
- 简化停用词处理:借助TF-IDF算法的内在机制对停用词“软过滤”,无需额外维护停用词表,降低部署复杂度;
二、模块接口性能
使用cProfile对程序性能进行分析,可视化如下:
可知main.py耗时最高,同时发现可优化函数read_file,优化思路如下:
- 若输入文件编码已知,则在命令行直接指定编码读取,跳过chardet检测。即加入以下代码:
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read().strip()
return content
except UnicodeDecodeError:
# UTF-8解码失败,进入编码检测流程
pass
三、模块单元测试
部分测试代码
1. 部分测试代码
(1) 文本重复率计算测试
class TestPlagiarismChecker(unittest.TestCase):
"""论文查重程序的测试用例"""
orig_path = "plag\orig.txt"
# -------------- 功能正确性测试 --------------
def test_complete_duplication(self):
"""测试完全重复的文本,预期相似度为1.00"""
# 选择测试文本
copy_path = "plag\orig.txt"
# 读取并处理文本
orig_text = read_file(self.orig_path)
copy_text = read_file(copy_path)
orig_processed = preprocess_text(orig_text)
copy_processed = preprocess_text(copy_text)
# 计算相似度
similarity = calculate_similarity(orig_processed, copy_processed)
# 断言结果
self.assertAlmostEqual(similarity, 1.00, places=2)
(2) 文本是否合理测试
def test_empty_text(self):
"""测试空文本(原文或抄袭文为空)"""
# 原文为空,抄袭文非空
copy_path = "plag\empty.txt"
# 定义一个函数来捕获SystemExit异常
with self.assertRaises(SystemExit) as cm:
# 调用可能触发异常的函数
read_file(copy_path)
# 检查异常的返回码是否为 1
self.assertEqual(cm.exception.code, 1)
(3) 编码兼容测试
def test_read_utf8(self):
"""测试读取UTF-8编码文件(基准测试)"""
file_path = os.path.join(self.test_dir, "test_utf_8.txt")
content = read_file(file_path)
self.assertEqual(content, test_content, "UTF-8文件读取内容不一致")
def test_read_gbk(self):
"""测试读取GBK编码文件"""
file_path = os.path.join(self.test_dir, "test_gbk.txt")
content = read_file(file_path)
self.assertEqual(content, test_content, "GBK文件读取内容不一致")
2. 测试数据构造思路
(1) 功能正确性测试:通过预设不同重复程度的文本(完全重复、部分重复、完全不重复),检查计算结果是否符合预期范围。
(2) 边界场景测试:覆盖异常输入情况(空文本、文件不存在),确保程序在极端条件下的稳定性;
(3) 编码兼容性测试:测试read_file函数对不同编码格式(UTF-8、GBK、GB2312)内容读取的准确性。
3. 测试覆盖率
四、模块异常处理
1. 命令行参数错误
(1) 设计目标:处理用户输入命令不合规的情况,并给予提醒
(2) 测试样例:
def test_command_line_args(self):
"""测试命令行参数错误时程序是否报错退出"""
# 设置错误的命令行参数(少于3个参数)
sys.argv = ['main.py', 'orig.txt']
# 导入main模块以触发参数检查
with self.assertRaises(SystemExit) as cm:
detector()
2. 输入文本不合规
(1) 设计目标:对于用户输入的文本为空或文化不存在的情况进行测试,并给予提醒
(2) 测试样例:
def test_empty_text(self):
"""测试空文本(原文或抄袭文为空)"""
# 原文为空,抄袭文非空
copy_path = "plag\empty.txt"
# 定义一个函数来捕获SystemExit异常
with self.assertRaises(SystemExit) as cm:
# 调用可能触发异常的函数
read_file(copy_path)
# 检查异常的返回码是否为 1
self.assertEqual(cm.exception.code, 1)
def test_file_not_found(self):
"""测试文件不存在时是否报错"""
# 故意使用不存在的文件路径
temp_dir = tempfile.mkdtemp()
non_existent_path = os.path.join(temp_dir, "nonexistent.txt")
# 读取不存在的文件应触发异常退出
with self.assertRaises(SystemExit):
read_file(non_existent_path)
3. 命令行含非法字符
(1) 设计目标:对于用户在命令行输入非法字符的场景进行处理
(2) 测试样例:
def test_invalid_chars_in_args(self):
"""测试命令行参数包含非法字符时的处理"""
# 包含非法字符的参数(如特殊符号、不可见字符等)
invalid_args = [
'main.py',
'orig.txt',
'orig_0.8_add\?*<>|.txt', # 包含Windows文件系统非法字符
'answer.txt'
]
sys.argv = invalid_args
# 预期程序会因文件操作错误退出
with self.assertRaises(SystemExit) as cm:
detector()
# 验证退出码为1(错误退出)
self.assertEqual(cm.exception.code, 1)