第一次个人编程作业

第一次个人编程作业github链接

一、PSP表格

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

二、计算模块接口

1.计算模块接口的设计与实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。

1.1函数构成:七个函数

  • def __init__(self): 初始化
  • def analyze(self, path): 分析处理每行敏感词,调用handle_sensitivewords函数和create_sensitivewordsmap(self, words)函数来实现
  • def handle_sensitivewords(self, line0, bushoupath1): 处理敏感词,先匹配中文部首,再获取拼音并组合
  • def transform(self, words, bs, bsflag): 将每个敏感词转化为拼音,并进行排列组合
  • def combine_char(self, cnt, wordslen, ch, value, swlist): 通过递归将完整拼音和首字母做排列组合,得到类似['xiej','xjiao']
  • def create_sensitivewordsmap(self, words): 创建敏感词树trie
  • def match_sensitivewords(self, text, linecnt1): 匹配敏感词

1.2流程图

1.3核心算法

DFA算法:基本思想是基于状态转移来检索敏感词,只需要扫描一次待检测文本,就能对所有敏感词进行检测。

把敏感词排列成树形结构,可以减少搜索次数。我们只需要遍历一次待检测文件,然后搜索有没有对应的子树。如果没有对应的子树,说明不是敏感词,则继续检测下一个字符;如果有相应的子树,就检测下一个字符在不在子树的子树中,如果整个词检测完都满足,就说明是敏感词。我们用字典来存储上述树形结构,在词末记录一个结束标志isEnd。

1.4设计思路

  • 首先要建立trie树。将处理后的词语内容和原词一起存入字典中,然后逐个字符建立trie树

    • 对中文文本的处理。由于中文文本中存在部分谐音替换、拼音替代、拼音首字母替代的敏感词(拼音不区分大小写),如 shan寨,栅寨,山Z等均可视为敏感词。中文文本中还存在少部分较难检测变形如繁体、拆分偏旁部首(只考虑左右结构)等。我的想法是对汉字进行拆分。利用pypinyin库将所有中文都转换为不带音调的拼音,这样就可以一次性解决谐音替换、拼音替代、繁体变形的问题了,全部转换成拼音,然后将拼音和对应的敏感词中文存入字典中。而对于偏旁部首,我的想法是先获取敏感词的每个字对应的左右两个偏旁部首,然后将两个偏旁部首当作两个字符组合起来与其对应的敏感词中文一同存入字典中。
    • 字典中要怎么存呢?因为每个汉语会对应多个不同的组合形式,所以应该把组合后的字符串存为key值,而汉语放在value中
  • 实例(以“你好”为例):

    • 转换拆解:'ni', 'n', 'hao', 'h', '亻', '尔', '女', '子'
    • 排列组合:nihao, nih, nhao, nh, 亻尔hao, 亻尔h, 亻尔女子, ni女子, n女子,
    • 存入字典
            {'nihao': '你好', 
            'nih': '你好', 
            'nhao': '你好', 
            'nh': '你好', 
            '亻尔hao': '你好', 
            '亻尔h': '你好',  
            '亻尔女子': '你好', 
            'ni女子': '你好', 
            'n女子': '你好'}



  • 将待检测文本与敏感词进行匹配

    • 我的做法是将所有文本内容都转换为英文或拼音再逐个字符进行匹配,这么做对于没有拆分部首的可以解决,但是对于拆分成部首的没有办法解决,因为时间和能力的限制,目前还没有实现偏旁部首进行的匹配,所以还没有想到怎么解决偏旁部首的问题。

    • 对于特殊符号的处理上。中文敏感词可能进行一些伪装,在敏感词中插入除字母、数字、换行的若干字符仍属于敏感词。如:当山寨为敏感词词汇时,山_寨,山@寨,山 寨,均可视为敏感词。英文文本不区分大小写,在敏感词中插入若干空格、数字等其他符号(换行、字母除外),也属于敏感词,如hello为敏感词时,he_llo,h%ell@o,he llo均为敏感词 。一开始我写了一个函数,然后对文本中的所有符号统一去除,但是我发现这种方法在还原原文敏感词时难以实现,而且没有必要去除。于是我转换思路,那就不去除,遇到符号就跳过,如果符号是夹在敏感词之间的就要记录下跳过的次数,便于还原原文得到时候能锁定

    • 在输出答案时,由于要统计敏感词总数,所以我将行数、真敏感词和含敏感词的原文语句,三个输出量存成一行字符串在列表中返回


1.5输出可视化:生成统计图(利用matplotlib)

2.计算模块接口部分的性能改进。记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2019、JProfiler或者Jetbrains系列IDE自带的Profiler的性能分析工具自动生成),并展示你程序中消耗最大的函数。

  • 性能分析图

  • 从图中可见,match_sensitivewords,即匹配敏感词函数耗时最多,该函数如下
# 匹配敏感词
    def match_sensitivewords(self, text, linecnt1):
        # text = text.strip()  # 敏感词去除首尾空格和换行
        ptr = 0     # 索引
        # 整行
        while ptr < len(text):
            nowmap = self.SenWordMap
            signflag = 0  # 判断符号是否夹在敏感词中
            swwords = ""  # 存入敏感词词库中的value
            cnt = 0
            # print("cnt===%d" % cnt)
            exitflag = 0
            # 对整行处理
            # print(text[ptr:])
            for char in text[ptr:]:     # 遍历每个词
                pinyinstr = ""
                for item0 in pypinyin.pinyin(char, style=pypinyin.NORMAL):
                    pinyinstr += "".join(item0)
                # print("ptr=%d,string=%s" % (ptr, pinyinstr))

                # 符号夹在敏感词中
                if signflag == 1 and pinyinstr[0] in sign:
                    cnt += 1
                    # print("cnt==%d" % cnt)
                    continue
                # 数字夹在夹在敏感词中
                if signflag == 1 and pinyinstr[0].isdigit():
                    cnt += 1
                    # print("cnt==%d" % cnt)
                    continue

                start = ptr     # 记录下敏感词在原文中匹配到的起始位置
                for ch in pinyinstr:
                    ch = ch.lower()     # 若碰到大写,先转化为小写
                    # 如果该字符在链表中
                    if ch in nowmap:
                        signflag = 1
                        swwords += ch
                        # 如果匹配到不是词尾,就进入子链表
                        if self.delimit not in nowmap[ch]:
                            nowmap = nowmap[ch]
                        # 如果匹配到是词尾,就退出循环
                        else:
                            self.totalwords += 1  # 敏感词总数加一
                            ptr += cnt  # 更新索引
                            end = ptr + 1   # 记录下敏感词在原文中匹配到的终止位置
                            textswwords = text[start:end]  # 原文敏感词
                            # 将三个值存为一行字符串,便于输出
                            answer.append("Line%d: <%s>%s" % (linecnt1, SenWordDict[swwords], textswwords))
                            signflag = 0
                            exitflag = 1
                            break
                    # 如果该字符不在,就不匹配,退出循环
                    else:
                        # swwords = ""
                        exitflag = 1
                        break
                if exitflag == 1:
                    break
                cnt += 1
            ptr += 1
        return self.totalwords

3.计算模块部分单元测试展示。展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。

单元测试一:match_sensitivewords

  • 利用assertEqual函数测试不同组合形式的敏感词
import unittest
import main

class MyTestCase(unittest.TestCase):
    def test_something(self):
        self.assertEqual(True, False)  # add assertion here
    def test_match(self):
        text = """城市如何留住daxuesheng,是这几年备受关注的公共话题。
        日**前,有媒体报道,2017年提出“百万大#%$#学生留汉创业就业计划”的武汉,又明确在“十四五”期间,每年留住30万名大$%学生,5年留住150万名大xs。
        日//前安徽省人社厅在答复政协委员“留住大xue生”的提案中提出,将实施在皖大學生婚姻帮扶工程。
        """
        res=["Line1: <大学生>daxuesheng", "Line2: <日前>日**前", "Line2: <大学生>大#%$#学生", "Line2: <大学生>大$%学生", "Line2: <大学生>大xs", "Line3: <日前>日//前", "Line3: <大学生>大xue生", "Line3: <大学生>大學生"]
        test = main.SensitiveWords()
        wordpath = r'E:\pycharmdemo\words1.txt'
        bushoupath = r'E:\pycharmdemo\bushou.txt'
        test.analyze(wordpath, bushoupath)
        linecnt = 1
        for line in text.splitlines():
            total = test.match_sensitivewords(line, linecnt)
            linecnt += 1
        self.assertEqual(res, main.answer)

if __name__ == '__main__':
    unittest.main()
  • 覆盖率如下

单元测试二:analyze

  • 利用assertEqual函数测试是否能生成对应的字典和trie树
import unittest
import main

class MyTestCase(unittest.TestCase):
    def test_something(self):
        self.assertEqual(True, False)  # add assertion here
    def testanalyze(self):
        wordpath = r'E:\pycharmdemo\words1.txt'
        bushoupath = r'E:\pycharmdemo\bushou.txt'
        test = main.SensitiveWords()
        test.analyze(wordpath, bushoupath)
        resdict = {'nihao': '你好', 'nih': '你好', 'ni女子': '你好', 'nhao': '你好', 'nh': '你好', 'n女子': '你好', '亻尔hao': '你好', '亻尔h': '你好', '亻尔女子': '你好'}
        restrie = {'n': {'i': {'h': {'a': {'o': {'isEnd': 0}}, 'isEnd': 0}, '女子': {'isEnd': 0}}, 'h': {'a': {'o': {'isEnd': 0}}, 'isEnd': 0}, '女子': {'isEnd': 0}}, '亻尔': {'女子': {'isEnd': 0}}}
        self.assertEqual(resdict, main.SenWordDict)
        self.assertEqual(restrie, test.SenWordMap)

if __name__ == '__main__':
    unittest.main()
  • 覆盖率如下

4.计算模块部分异常处理说明。在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。

  • IO异常处理:在读取文件时做了异常处理,如果打开失败了输出"Not Found:",成功则输出"Success"
   def test_ioerror(self):
      try:
          f = open('words.txt', encoding='utf-8')
      except IOError:
          print("Not Found:'words.txt'")
      else:
          print("Success")
          f.close()
  • 参数异常:当参数数量不等于3时,异常
  def test_canshu(self):
        if len(sys.argv)!=3:
            print("Error")
        else:
            return

三、心得

  • 刚看到题目的时候完全不懂要怎么处理,脑子非常懵,啊怎么办啊,我不会啊,我可能最后只能拿到写博客的12分吧。后来开始上网搜索,发现敏感词的过滤可以由好几种方法实现,有正则表达式、DFA算法、AC自动机算法,但是经过广泛地搜索以及学习这三个算法的基本知识以后,我了解到正则表达式对于处理大量的文本数据时会耗费大量的时间,检索效率低,而AC自动机算法对我来说有点难理解,而DFA算法只需要扫描一次待检测文本,就能对所有敏感词进行检测,所以效率比较高,所以最终选择用DFA算法实现。
  • 但是选择了以后,要怎么办,我要怎么做,我要从哪里入手,我要用什么语言来完成。浏览了网络上的大量资料后,我发现用python来处理文件好像比较容易,于是就确定用python来完成。因为我的python基础也不扎实,所以在编程时总是要搜索或者尝试。在完成任务的过程中,我感觉我的效率非常低,感觉每天都有花大把的时间做,但是感觉每天都没有做出什么东西来,还是毫无进展。除此之外,我感觉自己搜索资料的能力好像和别人差距很大,别人很容易就能搜到的东西,我要搜索很久才能找到。而且,我经常这调调那改改,但是最终不是这里错就是那里错,设置了一堆的辅助输出,一个一个看,一个一个地跟着代码走找问题,中间一度感觉很崩溃很心累,完全没有开始任务前的那种勇往直前,每天都会思考人生,思考为什么当时选课的时候这么义无反顾,思考这个课到底值不值得我花这么多的时间去研究,太难受了,更加坚定了我以后一定不要当开发型的程序员的想法。与舍友选择了不同的老师,于是就有了每天看着舍友做着为期10天的自我介绍作业,而我这10天做的是焦头烂额的敏感词过滤的编程作业,他们每日清闲,而我每天对着代码发愣,不禁感叹,为什么同是一门课区别竟会如此之大。
  • 在最后一天上传最终代码的时候,不小心把整个仓库删除了,心态崩了,反应过来,幸好之前有存一些过程代码在博客园草稿箱中,不然就完蛋了。
  • 不过这10天下来,我还是有所收获的。我的python能力得到了极大的提升,我发现python可以引用好多库好方便啊哈哈,搜索资料的能力大大提高,编程思维也有所提升。最终虽然成果不如别人的完整,但是也算是实现了,完成了一个我十天前觉得完全不可能完成的任务。

posted @ 2021-09-16 21:25  jasf  阅读(129)  评论(1编辑  收藏  举报