第一次个人编程作业

这个作业属于哪个课程 软件工程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工具得到测试覆盖率
image

image

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

    #判断三个文件是否存在
    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
posted @ 2024-03-12 16:01  dion_su_tian  阅读(138)  评论(0)    收藏  举报