第一次个人编程作业
| 这个作业属于哪个课程 | 软件工程2024 - 广东工业大学 |
|---|---|
| 这个作业要求在哪里 | 个人项目 - 作业 - 软件工程2024 - 班级博客 - 博客园 (cnblogs.com) |
| 这个作业的目标 | 学习掌握个人编程的规范的具体的步骤,学习用github等工具管理代码,学习使用工具分析代码,测试程序 |
| GitHub地址 | dionsc/3122004802: 《软件工程》作业:第一次个人编程作业 |
需求分析
设计一个可以通过命令行参数传递原文文件地址、抄袭文件地址、答案文件地址,计算抄袭文件相对于原文文件的重复率,并将结果以保留小数点后两位的形式输出到答案文件。
计算模块接口的设计与实现过程
对中文字段进行分词
使用jieba,对中文字符串进行精确模式分词。
def tokenize_zh(text):
return list(jieba.cut(text,cut_all=False))
计算两个文本的重复率
先将两个文本进行分词,再使用TF-IDF向量化器将文本转换为向量,然后计算两个文本之间的余弦相似度,用余弦相似度来表示两个文本的重复率。
def calculate_repeat_rate(orig, orig_add):
# 使用TF-IDF向量化器将文本转换为向量
vectorizer = TfidfVectorizer(tokenizer=tokenize_zh, lowercase=False)
tfidf_matrix = vectorizer.fit_transform([orig, orig_add])
# 计算两个文本之间的余弦相似度
similarity = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])[0][0]
return similarity
通过命令行参数的到文件地址后,读取原文文件和抄袭文件的内容,后将内容由calculate_repeat_rate()处理并返回两个文本的重复率,然后将重复率以保留小数点后两位的形式输出到目标文件。
程序的调用逻辑是\(solve() \rightarrow calculate\_repeat\_rate() \rightarrow tokenize\_zh()\)
计算模块接口部分的性能
通过line_profiler,测试程序,计算两个字符量大于 10^5 的文本的重复率
Timer unit: 1e-06 s
Total time: 0.615292 s
File: .\main.py
Function: tokenize_zh at line 12
Line # Hits Time Per Hit % Time Line Contents
==============================================================
12 @profile
13
14 #对中文字段进行分词
15 def tokenize_zh(text):
16 2 615291.7 307645.9 100.0 return list(jieba.cut(text,cut_all=False))
Total time: 0.636884 s
File: .\main.py
Function: calculate_repeat_rate at line 18
Line # Hits Time Per Hit % Time Line Contents
==============================================================
18 @profile
19 # 计算两个文本的重复率
20 def calculate_repeat_rate(orig, orig_add):
21
22 # 使用TF-IDF向量化器将文本转换为向量
23 1 36.2 36.2 0.0 vectorizer = TfidfVectorizer(tokenizer=tokenize_zh, lowercase=False)
24 1 635724.3 635724.3 99.8 tfidf_matrix = vectorizer.fit_transform([orig, orig_add])
25
26 # 计算两个文本之间的余弦相似度
27 1 1123.3 1123.3 0.2 similarity = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])[0][0]
28
29 1 0.2 0.2 0.0 return similarity
Total time: 0.638313 s
File: .\main.py
Function: solve at line 31
Line # Hits Time Per Hit % Time Line Contents
==============================================================
31 @profile
32 def solve(orig,orig_add,ans):
33
34 #判断三个文件是否存在
35 1 218.6 218.6 0.0 if os.path.exists(orig) and os.path.exists(orig_add) and os.path.exists(ans):
36 1 0.3 0.3 0.0 pass
37 else:
38 print("存在文件无法打开")
39 sys.exit(1)
40
41 # 读取原文
42 2 75.6 37.8 0.0 with open(orig, 'r', encoding='utf-8') as f:
43 1 59.6 59.6 0.0 orig_text = f.read()
44
45 # 读取抄袭文本
46 2 56.3 28.2 0.0 with open(orig_add, 'r', encoding='utf-8') as f:
47 1 104.8 104.8 0.0 orig_add_text = f.read()
48
49
50 # 计算查重率
51 1 636961.3 636961.3 99.8 repeat_rate = calculate_repeat_rate(orig_text, orig_add_text)
52
53 # 将查重率输出到答案文件
54 2 825.2 412.6 0.1 with open(ans, 'w', encoding='utf-8') as f:
55 1 11.6 11.6 0.0 f.write("{:.2f}".format(int(repeat_rate*100)/100))
三个程序的调用逻辑是\(solve() \rightarrow calculate\_repeat\_rate() \rightarrow tokenize\_zh()\)
但是三个程序程序的运行时间几乎相同,分析认为\(tokenize\_zh()\)函数的性能消耗最大
计算模块部分单元测试展示
单元测试代码
import unittest
from main import *
class MyTestCase(unittest.TestCase):
def setUp(self):
self.min_value = 0
self.max_value = 1
def test_calculate_repeat_rate(self):
result = calculate_repeat_rate("张树程","张树程")
self.assertEqual(result, 1)
result = calculate_repeat_rate("zsc","张树程")
self.assertEqual(result, 0)
result = calculate_repeat_rate("哈哈哈哈","哈哈")
self.assertTrue(self.min_value <= result <= self.max_value,
f"{result} is not within the range [{self.min_value}, {self.max_value}]")
result = calculate_repeat_rate("软件工程","计算机科学与技术")
self.assertEqual(result, 0)
result = calculate_repeat_rate("软件工程","信息安全")
self.assertEqual(result, 0)
result = calculate_repeat_rate("软件工程","网络工程")
self.assertEqual(result, 0)
result = calculate_repeat_rate("卓越班","伏羲班")
self.assertTrue(self.min_value <= result <= self.max_value,
f"{result} is not within the range [{self.min_value}, {self.max_value}]")
result = calculate_repeat_rate("广东工业大学","广工")
self.assertTrue(self.min_value <= result <= self.max_value,
f"{result} is not within the range [{self.min_value}, {self.max_value}]")
result = calculate_repeat_rate("广工","广东工业大学")
self.assertTrue(self.min_value <= result <= self.max_value,
f"{result} is not within the range [{self.min_value}, {self.max_value}]")
result = calculate_repeat_rate("一饭","五饭")
self.assertTrue(self.min_value <= result <= self.max_value,
f"{result} is not within the range [{self.min_value}, {self.max_value}]")
def test_solve(self):
solve(r"D:\桌面\orig.txt",r"D:\桌面\orig_0.8_add.txt",r"D:\桌面\ans.txt")
solve(r"D:\桌面\orig.txt",r"D:\桌面\orig_0.8_del.txt",r"D:\桌面\ans.txt")
solve(r"D:\桌面\orig.txt",r"D:\桌面\orig_0.8_dis_1.txt",r"D:\桌面\ans.txt")
solve(r"D:\桌面\orig.txt",r"D:\桌面\orig_0.8_dis_10.txt",r"D:\桌面\ans.txt")
solve(r"D:\桌面\orig.txt",r"D:\桌面\orig_0.8_dis_15.txt",r"D:\桌面\ans.txt")
if __name__ == '__main__':
unittest.main()
构造数据思路:构造小数据来精确度测试,大数据来进行性能测试
利用coverage工具得到测试覆盖率


计算模块部分异常处理说明
#判断三个文件是否存在
if os.path.exists(orig) and os.path.exists(orig_add) and os.path.exists(ans):
pass
else:
print("存在文件无法打开")
sys.exit(1)
当命令行传入的文件地址有误时,给出反馈,并终止程序
单元测试代码:
def test_solve(self):
solve(r"orig.txt",r"orig_0.8_add.txt",r"ans.txt")
PSP表格
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 估计这个任务需要多少时间 | 5 | 10 |
| Development | 开发 | ||
| · Analysis | · 需求分析 (包括学习新技术) | 30 | 180 |
| · Design Spec | · 生成设计文档 | 60 | 60 |
| · Design Review | · 设计复审 | 30 | 45 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
| · Design | · 具体设计 | 20 | 20 |
| · Coding | · 具体编码 | 60 | 75 |
| · Code Review | · 代码复审 | 30 | 20 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 60 | 75 |
| Reporting | 报告 | ||
| · Test Repor | · 测试报告 | 60 | 60 |
| · Size Measurement | · 计算工作量 | 60 | 60 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 60 |
| · 合计 | 495 | 675 |

浙公网安备 33010602011771号