第一次个人编程作业

这个作业属于哪个课程 计科22级34班
这个作业要求在哪里 个人项目
这个作业的目标 1.设计一个查重算法。
2. 了解并学习项目的PSP表格
3. 学习如何运用github进行代码管理
4. 学习使用性能分析工具,分析代码性能
5. 学习如何进行单元测试

我的github仓库链接:https://github.com/zfirejs/3122004631/tree/master

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 30
· Estimate · 估计这个任务需要多少时间 20 30
Development 开发 900 990
· Analysis · 需求分析 (包括学习新技术) 280 310
· Design Spec · 生成设计文档 30 30
· Design Review · 设计复审 50 55
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 25
· Design · 具体设计 70 75
· Coding · 具体编码 350 365
· Code Review · 代码复审 40 50
· Test · 测试(自我测试,修改代码,提交修改) 60 80
Reporting 报告 85 90
· Test Repor · 测试报告 60 50
· Size Measurement · 计算工作量 15 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 20
· 合计 1005 1110

计算模块接口设计与实现过程

过程描述

代码实现了一个基于文本相似度的查重功能,用于比较两篇中文文本的相似度,并将相似度结果输出到文件中。程序中使用了jieba库进行中文分词处理,再利用余弦相似度计算两篇文本的相似程度。

代码组成

代码中只有一个类:DuplicateChecking

该类包含以下几个函数:
1.init:类的构造函数,初始化类变量,用于保存原始文本和待查重文本的内容及分词列表。
2.read_file:读取原始文件和待查重文件的内容,并将其保存为类变量 original_text 和 compare_text。。
3.long_text_preprocess:文本的预处理函数,去除标点符号后使用 jieba.analyse.extract_tags 提取文本中的关键词(20 个),并将关键词存储在 original_list 和 compare_list 中。
4.text_checking:核心查重函数,分别调用文件读取(read_file)、文本预处理(long_text_preprocess),然后构建词频向量并计算余弦相似度。最后输出相似度结果到指定文件。

算法核心

将文本去除标点符号后使用,提取文本中的关键词(20 个),然后构建词频向量并计算余弦相似度,从而得出文本相似度。

流程图

计算模块接口部分的性能改进

改进思路

本程序的计算模块的主要性能瓶颈为文本的输入,其中短文本可以使用python文件IO类的read()方法,但是对于较长文本的输入,则需要使用readlines()方法实现逐行输入,避免因内存容量导致的IO速率过慢

改进部分代码

try:
    with open(original_text_address, "r", encoding="utf-8") as file1:
        self.original_list = file1.readlines()
        # 将读取的行拼接成单个字符串存储
        self.original_text = self.original_text.join(self.original_list)
except FileNotFoundError:
    print("未找到原始文本文件 " + original_text_address + " 请重试")
    original_text_address = ""       

性能分析图

消耗最大的函数(text_checking)

def text_checking(self):
    """
    主功能方法,负责根据文本长度选择适合的预处理方法。
    长文本(超过1000字符)会调用 `long_text_preprocess` 方法,短文本会调用 `short_text_preprocess` 方法。
    最终使用余弦相似度计算两个文本之间的相似度。
    """
    original_vector = []
    compare_vector = []

    # 读取文件,检查读取是否成功
    if not self.read_file():
        return False

    # 根据文本长度判断选择预处理方法,超过1000字符的文本被视为长文本
    if len(self.original_text) > 1000 or len(self.compare_text) > 1000:
        self.long_text_preprocess()
    else:
        self.short_text_preprocess()

    # 合并分词列表并去重,创建词汇表
    self.word_store = list(set(self.original_list + self.compare_list))

    # 构建词频向量
    for word in self.word_store:
        original_vector.append(self.original_list.count(word))
        compare_vector.append(self.compare_list.count(word))

    original_vector = numpy.array(original_vector)
    compare_vector = numpy.array(compare_vector)

    # 使用 scipy 库的余弦相似度函数计算相似度
    cos_sim = 1 - spatial.distance.cosine(original_vector, compare_vector)

    # 将相似度写入文件并提示用户
    print("请输入查重结果文件输出的地址:")
    duplicate_data_address = input("请输入抄袭文本的绝对路径")#"C:\\Users\\周晨佳\\Desktop\\ceshi\\shuchu.txt"
    try:
        with open(duplicate_data_address, "w", encoding="utf-8") as file:
            file.write("待查文本与原文本的相似度为:" + str(round(cos_sim, 2)))
            print("查重结果已输出到文件!")
    except IOError:
        print("查重结果文件创建失败,请检查路径并重试。")
    return True

计算模块部分单元测试展示:

测试思路

单元测试使用pycharm自带的unittest测试框架,运用unittest框架的TestCase类,在类中实例化测试函数,运行测试python文件,实现main程序的测试。
对于项目中的输入模块,引入unittest测试框架中的patch类模拟用户的输入,实现输入模块的测试。
单元测试通过模拟用户的行为,以及对于main.py中DuplicateChecking查重类中的每个函数运行流程进行模拟,使用框架自带的断言函数assertEqual(),实现对项目中的类及其方法的测试。

测试代码(test.py)

import unittest
import random
from src.main import DuplicateChecking
from unittest.mock import patch  # 用于模拟输入
# 记录测试文本地址
original_text = [r'C:\Users\周晨佳\Desktop\ceshi\orig.txt', 'cuowu1', 'cuowu2',
                 'cuowu3', 'cuowu4']
test_text = [r'C:\Users\周晨佳\Desktop\ceshi\orig_0.8_add.txt',
             r'C:\Users\周晨佳\Desktop\ceshi\orig_0.8_del.txt',
             r'C:\Users\周晨佳\Desktop\ceshi\orig_0.8_dis_1.txt',
             r'C:\Users\周晨佳\Desktop\ceshi\orig_0.8_dis_10.txt',
             r'C:\Users\周晨佳\Desktop\ceshi\orig_0.8_dis_15.txt',
             'cuowu1', 'cuowu2','cuowu3', 'cuowu4']

class MyTestCase(unittest.TestCase):
    @patch('builtins.input')
    def test_IO(self, mock_input):
        result = DuplicateChecking()# 实例化测试对象
        mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)]]  # 正确的输入
        self.assertEqual(result.read_file(), True)# 断言测试判断
        mock_input.side_effect = [original_text[random.randint(1, 4)], test_text[random.randint(5, 8)]]  # 错误的输入
        self.assertEqual(result.read_file(), False)

    @patch('builtins.input')
    def test_long_text_preprocess(self, mock_input):
        result = DuplicateChecking()
        mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)]]  # 正确的输入
        result.read_file()
        self.assertEqual(result.long_text_preprocess(), True)

    @patch('builtins.input')
    def test_text_checking(self, mock_input):
        result = DuplicateChecking()
        mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)],
                                  r'C:\Users\周晨佳\Desktop\ceshi\output.txt']  # 正确的输入
        self.assertEqual(result.text_checking(), True)

        mock_input.side_effect = [original_text[random.randint(1, 4)], test_text[random.randint(5, 8)],
                                  r'C:\Users\周晨佳\Desktop\ceshi\output.txt']  # 错误的输入
        self.assertEqual(result.text_checking(), False)

if __name__ == '__main__':
    unittest.main()

测试覆盖率:

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

1.输入异常(文件不存在)

 try:
    with open(original_text_address, "r", encoding="utf-8") as file1:
        self.original_list = file1.readlines()
        # 将读取的行拼接成单个字符串存储
        self.original_text = self.original_text.join(self.original_list)
except FileNotFoundError:
    print("未找到原始文本文件 " + original_text_address + " 请重试")
    original_text_address = ""   

输入异常对应的单元测试

@patch('builtins.input')
def test_IO(self, mock_input):
    result = DuplicateChecking()# 实例化测试对象
    mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)]]  # 正确的输入
    self.assertEqual(result.read_file(), True)# 断言测试判断
    mock_input.side_effect = [original_text[random.randint(1, 4)], test_text[random.randint(5, 8)]]  # 错误的输入
    self.assertEqual(result.read_file(), False)

2.输出异常(文件不存在)

try:
    with open(duplicate_data_address, "w", encoding="utf-8") as file:
        file.write("待查文本与原文本的相似度为:" + str(round(cos_sim, 2)))
        print("查重结果已输出到文件!")
except IOError:
    print("查重结果文件创建失败,请检查路径并重试。")

输出异常对应的单元测试

@patch('builtins.input')
def test_text_checking(self, mock_input):
    result = DuplicateChecking()
    mock_input.side_effect = [original_text[0], test_text[random.randint(0, 4)],
                              r'C:\Users\周晨佳\Desktop\ceshi\output.txt']  # 正确的输入
    self.assertEqual(result.text_checking(), True)
    mock_input.side_effect = [original_text[random.randint(1, 4)], test_text[random.randint(5, 8)],
                              r'C:\Users\周晨佳\Desktop\ceshi\output.txt']  # 错误的输入
    self.assertEqual(result.text_checking(), False)

心得

本次作业使用到了一些专业的关于测试项目的工具,让我学会了如何进行代码覆盖率的测试。最大的收获就是学习了如何进行项目的单元测试,学会了如何编写测试代码,怎么样才能尽可能提高代码覆盖率。总的来说,这次作业让我对软件工程中的测试有了更深的理解,让我受益匪浅。

posted @   FIREJS  阅读(33)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示