第一次个人编程作业

作业链接

一、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 5 5
· Estimate · 估计这个任务需要多少时间 5 5
Development 开发 1620 2010
· Analysis · 需求分析 (包括学习新技术) 180 300
· Design Spec · 生成设计文档 10 10
· Design Review · 设计复审 30 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 60 30
· Design · 具体设计 180 120
· Coding · 具体编码 600 1440
· Code Review · 代码复审 500 10
· Test · 测试(自我测试,修改代码,提交修改) 60 70
Reporting 报告 95 155
· Test Repor · 测试报告 60 120
· Size Measurement · 计算工作量 5 5
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 30
· 合计 1720 2170

二、计算模块接口

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

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

代码组织
包括以下2个类:

WordMap类 - 构建DFA树

  • addToWordSet函数 - 将敏感词放入到HashSet中
  • initWordMap函数 - 初始化敏感词库

Tool类 - 使用DFA树以及拼音、部首的转化

  • getPyWord函数 - 中文敏感词拼音替代、拼音首字母替代
  • initDictMap函数 - 初始化部首字典
  • getBsWord函数 - 中文敏感词部首拆分
  • checkKeyword函数 - 敏感词检测

关键算法
关键函数有敏感词检测checkKeyword函数、getPyWord函数、getBsWord函数等。
其中getPyWord函数主要调用库来实现(jpinyin.jar是我目前找到最全的),getBsWord函数主要是添加汉语拆字的字典,来检测偏旁部首拆分。
而checkKeyword函数采用了DFA算法 ,即确定有穷自动机,通过动作和当前状态得到下一个状态,即event+state=nextstate。在实现敏感词过滤的过程中,我们必须要减少运算,而DFA在DFA算法中几乎没有什么计算,只有状态的转换。
实现DFA算法的流程为:

  • 词库整理:将敏感词放入到HashSet中
  • 构建DFA树:详见下面的流程图
  • DFA树的使用:逐个检测目标词中的字,最后一个字在树中且满足isEND=1,则是敏感词

构建DFA树流程图

        int matchNum = 0;           //  敏感词数量
        String wordTxt = "";        //  检测到的敏感词
        int beginIndex = 100000;    //  文本首部
        int endIndex = 0;           //  文本尾部
        char word;
        Map currMap = keywordMap;
        Map prevMap;
        for(int i = 0; i < txt.length(); i++){
            word = txt.charAt(i);
            if(word >= 'A' && word <= 'Z'){
                //  大小写字母替代
                word -= 'A'-'a';
            }else if(ChineseHelper.isTraditionalChinese(word)){
                //  繁体字替代
                word = ChineseHelper.convertToSimplifiedChinese(word);
            }
            //  判断该字是否存在于敏感词库中
            prevMap = currMap;
            currMap = (Map) currMap.get(word);
            if(currMap != null){     //  存在
                if(beginIndex > i){
                    beginIndex = i;
                }
                endIndex++;
                wordTxt += word;
                //  判断该字是否为结尾字
                if("1".equals(currMap.get("isEnd"))){
                    if(i != txt.length() - 1 && (Map) currMap.get(txt.charAt(i+1))!=null){    //   最大规则
                        continue;
                    }
                    matchNum++;
                    ansArr.add("Line" + line + ": <" + wordTxt + ">"+txt.substring(beginIndex, beginIndex + endIndex));
                    beginIndex = 100000;
                    endIndex = 0;
                    wordTxt = "";
                    currMap = keywordMap;
                }
            }else{                 //  不存在
                if(endIndex > 0 && (word+"").matches("[^(a-zA-Zu4e00-u9fa5)]")){
                    //  过滤伪装字
                    endIndex++;
                    currMap = prevMap;
                } else{
                    beginIndex = 100000;
                    endIndex = 0;
                    wordTxt = "";
                    currMap = keywordMap;
                }
            }
        }
        return matchNum;
    }

独到之处有运算量小、检测速度快、便于维护 和几个难题:

  • 敏感词输出怎么查找原词
  • 最小匹配和最大匹配原则有什么区别
  • 拼音替代功能要在什么时候实现(最后选择添加进HashSet中)

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

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


性能分析图.png
消耗最大的函数是matches函数,主要是jpinyin使用。一方面,很多功能都依赖于这个函数实现,函数比较累赘,考虑减少其他功能的调用;另一方面,jpinyin下的函数消耗也很大,考虑换一个更优秀的库?

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

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

测试拼音、拼音首字母替代敏感词的添加

	@Test
    public void testGetPyWord() throws Exception {

        //  敏感词
        HashSet<String> keyWordsSet = new HashSet<>();
        keyWordsSet.add("好人");

        //  预期答案    共9个
        HashSet<String> ansSet = new HashSet<>();
        ansSet.add("好人");
        ansSet.add("好ren");
        ansSet.add("好r");
        ansSet.add("hao人");
        ansSet.add("h人");
        ansSet.add("haoren");
        ansSet.add("haol");
        ansSet.add("hren");
        ansSet.add("hl");

        Tool tool = new Tool();
        tool.getPyWord(0,"好人",keyWordsSet);
        keyWordsSet.equals(ansSet);
    }

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

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

IOException
读写异常:在使用流、文件和目录访问信息时引发的异常

PinyinException
拼音异常:发生在无法转换汉字拼音、拼音首字母的场景

三、心得

Java语言真的很难啊,相比之下Python简单很多。要是重来一次,我会选择Python,毕竟两种语言都是菜鸟水平。但最后也实现了基本需求,就是单元测试部分还没做得很清楚,IDEA相关的工具用得也很懵,希望下次效率能够提升吧!

原文地址:https://www.cnblogs.com/dump16/p/15233342.html