JAVA中的DFA算法构建敏感词树,从0开始!
2017-12-05 15:00
525 查看
前一阵做项目涉及到敏感词检测问题,因为这个模块要求不是很高,应用场景就是用户在文章下面评论,或者发布留言等需要管理员进行审核,所以要在给管理员审核的页面对评论内容中的敏感词标红、判断内容是否有敏感词等,这样能让管理员快速审核。
因为我这个敏感词模块涉及到的东西不是很多,所以做的可能比较简单,粗糙,第一次写博客,请谅解!有什么建议欢迎提出来!
下面说一下我的构建过程:
首先从第一步建表开始
项目需求是需要对敏感词有两种应用方式,一种是阻止,一种是替换。这两种方式不一定适用于所有模块,现在是针对留言、评论等,但是以后可能会涉及到别的模块。但是阻止的优先级是比替换高的。所以我当初的设想就是,表字段中有两个字段存该敏感词是阻止哪个模块,替换哪个模块,如果有需要自动代理,不需要人工审核的,只需要拿内容来,先进行检测,检测出敏感词,再拿敏感词去数据库取对象,判断该敏感词对于正在应用的模块是敏感还是替换,首先是要进行敏感词匹配。
附上我的SQL:
然后就是POJO创建,这里不多说,接下来说敏感词树的构建问题:
根据我的大致计算,4万条敏感词加载到缓存其实占用的内存还是很小的,实测4万条敏感词树构建大概200ms以内,足够满足我的需求;
首先建立一个敏感词工具类:
这棵树因为是要在项目启动的时候进行初始化,在类中声明一个静态的map;
然后下一步就是用sensitiveService去数据库一次拿到所有的敏感词;
为防止数据库有脏数据,导致有重复敏感词(新增敏感词的时候,名称有唯一性检测,只是为了防止脏数据),这里先用set去重;
下一步就是初始化敏感词树;
下面看下addSensitiveWordToHashMap()方法,这个方法是插入敏感词方法;
当插入一条敏感词的时候,比如“小明是狗”,那么会先去map中get小,不存在就构建一个map放这个小,标识该词还没有结束,如果存在,就去小对应的map中get明以此类推,当到狗的时候,标识该词已经结束了;添加isEend为1;
那么我这里为什么要分好几个方法写呢,主要是为了项目需求,因为我们敏感词是有个管理的页面,管理员可以对某个敏感词进行添加,修改,删除操作的,所以当管理员进行这些操作的时候,这个树肯定也是要随机应变的,我这里的做法就是对外提供两个公开方法,一个添加敏感词,一个重置敏感词树, 那么在新增敏感词的时候我只需要对树中进行添加一条就行了, 删除和修改,就重置一下敏感词树(反正咱们构建时间也不长—_—)......
先来看一下对外封装的添加敏感词方法:
树构建好了,那么下一步就是检测了:
对外提供方法有:1、该字符串是否存在敏感,2、找出字符串中所有的敏感词,3、替换字符串中的所有敏感词
他们都是基于一个方法,就是敏感词检测方法:
这个方法返回的是该字符串中出现敏感词的起始位置;
然后封装一层,判断字符串中是否有敏感词:
还一个方法就是获取字符串中所有的敏感词:
还有一个替换敏感词方法:
下面也贴一下这个方法吧
到此整个工具类就算是能对外提供使用了,只需要在数据库插入敏感词就能用了;
做的比较粗糙,因为时间问题,也比较赶,可能以后需求还会改;
当然这个也是有缺陷的!
缺陷就是:1、空格也会列入检测范围,就是某个词可能最后加上空格也会被构建到树中,这样有好处,也有坏处
2、没有引入距离概念,比如;词是“小明是狗”, 那么“小明.是狗”、“小明 是 狗 ”。。。。。等等这样的都不会被检测出来,所以有很大的局限性,请根据自己的需求来判断!
3、。。。。。。。还有其他的我就不多说了。个人理解 嘿嘿~~~~~~~
新手第一次写博客~~~,如有什么技术的语言、或者代码纰漏,请指正!!! 但是别喷我~~~喷我我会很伤心~
因为我这个敏感词模块涉及到的东西不是很多,所以做的可能比较简单,粗糙,第一次写博客,请谅解!有什么建议欢迎提出来!
下面说一下我的构建过程:
首先从第一步建表开始
项目需求是需要对敏感词有两种应用方式,一种是阻止,一种是替换。这两种方式不一定适用于所有模块,现在是针对留言、评论等,但是以后可能会涉及到别的模块。但是阻止的优先级是比替换高的。所以我当初的设想就是,表字段中有两个字段存该敏感词是阻止哪个模块,替换哪个模块,如果有需要自动代理,不需要人工审核的,只需要拿内容来,先进行检测,检测出敏感词,再拿敏感词去数据库取对象,判断该敏感词对于正在应用的模块是敏感还是替换,首先是要进行敏感词匹配。
附上我的SQL:
CREATE TABLE `base_sys_sensitive` ( `ID` int(11) unsigned NOT NULL AUTO_INCREMENT, `NAME` varchar(200) DEFAULT NULL COMMENT '敏感词', `REPLACENAME` varchar(200) DEFAULT NULL COMMENT '替换词', `PREVENTOBJ` varchar(100) DEFAULT NULL COMMENT '阻止对象(适用范围)', `REPLACEOBJ` varchar(100) DEFAULT NULL COMMENT '替换对象(适用范围)', `CREATEUSER` int(11) unsigned DEFAULT NULL, `CREATETIME` timestamp NULL DEFAULT NULL, `UPDATEUSER` int(11) unsigned DEFAULT NULL, `UPDATETIME` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, `DELFLAG` tinyint(1) unsigned NOT NULL DEFAULT '0', `REMARK` varchar(200) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`ID`) ) ENGINE=MyISAM AUTO_INCREMENT=250596 DEFAULT CHARSET=utf8;
然后就是POJO创建,这里不多说,接下来说敏感词树的构建问题:
根据我的大致计算,4万条敏感词加载到缓存其实占用的内存还是很小的,实测4万条敏感词树构建大概200ms以内,足够满足我的需求;
首先建立一个敏感词工具类:
这棵树因为是要在项目启动的时候进行初始化,在类中声明一个静态的map;
然后下一步就是用sensitiveService去数据库一次拿到所有的敏感词;
为防止数据库有脏数据,导致有重复敏感词(新增敏感词的时候,名称有唯一性检测,只是为了防止脏数据),这里先用set去重;
private static Set<String> readSensitiveWordFile(){ Set<String> set = null; set = new HashSet<String>(); List<Sensitive> sensitiveList = sensitiveService.queryAll(); for(Sensitive sensitive : sensitiveList){ set.add(sensitive.getName()); } return set; }到这里的时候整个数据库中的敏感词都已经拿到了;
下一步就是初始化敏感词树;
@SuppressWarnings({ "rawtypes"}) private static Map addSensitiveWordToHashMap(Set<String> keyWordSet) { Map sensitiveWordMap = new HashMap(); String key = null; // 迭代keySet Iterator<String> iterator = keyWordSet.iterator(); while(iterator.hasNext()){ key = iterator.next(); addSensitiveWordToHashMap(sensitiveWordMap, key); } return sensitiveWordMap; }这个方法主要是对敏感词set进行迭代,一条条的插入到树中;
下面看下addSensitiveWordToHashMap()方法,这个方法是插入敏感词方法;
@SuppressWarnings({ "rawtypes", "unchecked" }) private static void addSensitiveWordToHashMap(Map map, String sensitive){ Map<String, String> newWorMap = null; for(int i = 0 ; i < sensitive.length() ; i++){ // 转换成char型 char keyChar = sensitive.charAt(i); Object wordMap = map.get(keyChar); // 如果存在该key,直接赋值 if (wordMap != null) { map = (Map) wordMap; }else { // 不存在就构建一个map,同时将isEnd设置为0 newWorMap = new HashMap<String,String>(); // 不是最后一个 newWorMap.put("isEnd", "0"); map.put(keyChar, newWorMap); map = newWorMap; } if(i == sensitive.length() - 1){ // 最后一个 map.put("isEnd", "1"); } } }这个我的敏感词树就一条条的构建成功了;
当插入一条敏感词的时候,比如“小明是狗”,那么会先去map中get小,不存在就构建一个map放这个小,标识该词还没有结束,如果存在,就去小对应的map中get明以此类推,当到狗的时候,标识该词已经结束了;添加isEend为1;
那么我这里为什么要分好几个方法写呢,主要是为了项目需求,因为我们敏感词是有个管理的页面,管理员可以对某个敏感词进行添加,修改,删除操作的,所以当管理员进行这些操作的时候,这个树肯定也是要随机应变的,我这里的做法就是对外提供两个公开方法,一个添加敏感词,一个重置敏感词树, 那么在新增敏感词的时候我只需要对树中进行添加一条就行了, 删除和修改,就重置一下敏感词树(反正咱们构建时间也不长—_—)......
先来看一下对外封装的添加敏感词方法:
public static void addSensitiveWordToHashMap(String sensitive){ if(sensitiveWordMap == null){ sensitiveWordMap = addSensitiveWordToHashMap(readSensitiveWordFile()); } addSensitiveWordToHashMap(sensitiveWordMap, sensitive); }再来看一下重新构建树的方法:
public static void resetSensitiveWordTree(){ sensitiveWordMap = addSensitiveWordToHashMap(readSensitiveWordFile()); }重新构建就是重新去数据库取一次,然后构建;
树构建好了,那么下一步就是检测了:
对外提供方法有:1、该字符串是否存在敏感,2、找出字符串中所有的敏感词,3、替换字符串中的所有敏感词
他们都是基于一个方法,就是敏感词检测方法:
@SuppressWarnings({ "rawtypes"}) public static int checkSensitiveWord(String txt, int beginIndex){ if(StringUtils.isBlank(txt)){ return IConstant.ZERO; } if(sensitiveWordMap == null){ sensitiveWordMap = addSensitiveWordToHashMap(readSensitiveWordFile()); } //防止敏感词只有1个 boolean flag = false; int matchFlag = 0; char word = 0; Map nowMap = sensitiveWordMap; for(int i = beginIndex; i < txt.length() ; i++){ word = txt.charAt(i); // 获取指定key nowMap = (Map) nowMap.get(word); // 存在就判断是否是最后一个 if (nowMap != null) { // 找到相应key,匹配标识+1 matchFlag++; // 如果为最后一个匹配规则,结束循环,返回匹配标识数 if ("1".equals(nowMap.get("isEnd"))) { // 结束标志位为true flag = true; } }else { // 不存在,直接返回 break; } } // 长度必须大于等于1 if (matchFlag < 2 || !flag) { matchFlag = 0; } return matchFlag; }
这个方法返回的是该字符串中出现敏感词的起始位置;
然后封装一层,判断字符串中是否有敏感词:
public static boolean isContaintSensitiveWord(String txt){ if(StringUtils.isBlank(txt)){ return false; } boolean flag = false; for(int i = 0 ; i < txt.length() ; i++){ // 判断是否包含敏感字符 int matchFlag = checkSensitiveWord(txt, i); // 大于0存在,返回true b1ed if (matchFlag > 0) { flag = true; } } return flag; }
还一个方法就是获取字符串中所有的敏感词:
public static Set<String> getSensitiveWord(String txt) { if(StringUtils.isBlank(txt)){ return null; } Set<String> sensitiveWordList = new HashSet<String>(); for(int i = 0 ; i < txt.length() ; i++){ // 判断是否包含敏感字符 int length = checkSensitiveWord(txt, i); // 存在就加入set中 if (length > 0) { sensitiveWordList.add(txt.substring(i, i+length)); // 减1因为for会自增 i = i + length - 1; } } return sensitiveWordList; }这里的做法就是对字符串进行循环,找到一个敏感词就截掉,再循环,一直到没有敏感词,这里用set是防止检测到重复敏感词;
还有一个替换敏感词方法:
public static String replaceSensitiveWord(String txt) { if(StringUtils.isBlank(txt)){ return null; } String resultTxt = txt; // 获取所有的敏感词 Set<String> set = getSensitiveWord(txt); Iterator<String> iterator = set.iterator(); String word = null; String replaceString = null; while (iterator.hasNext()) { word = iterator.next(); replaceString = getReplaceChars(word); resultTxt = resultTxt.replaceAll(word, replaceString); } return resultTxt; }这里会用到一个getReplaceChars()方法,这个方法是获取敏感词对于的替换词,因为我的需求是要替换词准确到某个词,所有敏感词的替换词都对应的存在数据库;
下面也贴一下这个方法吧
public static String getReplaceChars (String name){ if(StringUtils.isBlank(name)){ return null; } Sensitive sensitive = new Sensitive(); sensitive.setName(name); sensitive = sensitiveService.queryOne(sensitive); if(sensitive != null){ String replaceTxt = sensitive.getReplacename(); if(StringUtils.isNotBlank(replaceTxt)){ return replaceTxt; } } return null; }这里其实就是一个拿敏感词去数据库取替换词的过程;
到此整个工具类就算是能对外提供使用了,只需要在数据库插入敏感词就能用了;
做的比较粗糙,因为时间问题,也比较赶,可能以后需求还会改;
当然这个也是有缺陷的!
缺陷就是:1、空格也会列入检测范围,就是某个词可能最后加上空格也会被构建到树中,这样有好处,也有坏处
2、没有引入距离概念,比如;词是“小明是狗”, 那么“小明.是狗”、“小明 是 狗 ”。。。。。等等这样的都不会被检测出来,所以有很大的局限性,请根据自己的需求来判断!
3、。。。。。。。还有其他的我就不多说了。个人理解 嘿嘿~~~~~~~
新手第一次写博客~~~,如有什么技术的语言、或者代码纰漏,请指正!!! 但是别喷我~~~喷我我会很伤心~
相关文章推荐
- Java 敏感词替换-dfa算法,效率高
- 简单实现java DFA算法对敏感词过滤
- Java使用DFA算法实现敏感词过滤
- DFA算法实现Java敏感词过滤
- 高效敏感词过滤JAVA实现(DFA算法)
- Java实现DFA算法对敏感词、广告词过滤功能
- Java DFA算法实现敏感词过滤
- Java使用DFA算法实现过滤多家公司自定义敏感字功能详解
- java实现敏感词过滤 dfa算法实现
- java实现敏感词过滤(DFA算法)
- DFA算法 及java版本实现敏感词过滤
- Java 利用DFA算法 屏蔽敏感词
- Java 利用DFA算法 屏蔽敏感词
- DFA算法 及java版本实现敏感词过滤
- Java实现DFA算法 实现敏感词过滤
- Java 利用DFA算法 屏蔽敏感词
- Java实现DFA算法进行敏感词过滤
- Java实现DFA算法对敏感词、广告词过滤功能示例
- java利用DFA算法实现敏感词过滤功能
- DFA算法实现Java敏感词过滤