敏感词文字过滤是一个网站必不可少的功能,如何设计一个好的、高效的过滤算法是非常有必要的。作为一般开发人员来说首先考虑的肯定是简单的匹配,这样是可以实现功能,但效率比较慢,在高级一点的就是正则表达式,比前一个好一点,但终究还是一丘之貉,非常遗憾,两种方法都不可取。当然,在我意识里没有我也没有认知到那个算法可以解决问题,但是百度知道,以下就是学习的DFA算法简单介绍和功能实现。
DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。 其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。 但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。
具体功能:
将敏感词汇保存在map中。对敏感词汇进行过滤,将敏感词变为“*”。对无意义符号进行忽略处理。 敏感词数据结构: { 王:{ isEnd: false 八:{ isEnd:false 蛋:{ isEnd:true } } } } 无意义符号数据结构: { "@":Null (空结构体) }封装工具类:
public class SensitiveWordUtil { /** * 敏感词匹配规则 */ private static final int MinMatchTYpe = 1; //最小匹配规则,如:敏感词库["我是人","我是人么"],语句:"你猜我是人么",匹配结果:我是[我是人]么 public static final int MaxMatchType = 2; //最大匹配规则,如:敏感词库["我是人","我是人么"],语句:"你猜我是人么",匹配结果:我是[我是人么] /** * 敏感词集合 */ private static HashMap sensitiveWordMap; /** * 初始化敏感词库,构建DFA算法模型 * * @param sensitiveWordSet set */ private static synchronized void init(Set<String> sensitiveWordSet) { sensitiveWordMap = new HashMap(sensitiveWordSet.size()); //初始化敏感词容器,减少扩容操作 String key; Map nowMap; Map<String, String> newWorMap; for (String s : sensitiveWordSet) { //迭代sensitiveWordSet key = s; //关键字 nowMap = sensitiveWordMap; for (int i = 0; i < key.length(); i++) { char keyChar = key.charAt(i); //转换成char型 Object wordMap = nowMap.get(keyChar); //库中获取关键字 if (wordMap != null) { //如果存在该key,直接赋值,用于下一个循环获取 nowMap = (Map) wordMap; } else { newWorMap = new HashMap<>(); //不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个 newWorMap.put("isEnd", "0"); //不是最后一个 nowMap.put(keyChar, newWorMap); nowMap = newWorMap; } if (i == key.length() - 1) { nowMap.put("isEnd", "1"); //最后一个 } } } } /** * 判断文字是否包含敏感字符 * @param txt 要过滤的文章 * @param matchType 过滤规则 * @param list 脏词库 * @return */ public static boolean contains(String txt, int matchType, List<String> list) { if (sensitiveWordMap.size() <= 0){ System.out.println("开始初始化平台脏数据"); if (list.size() > 0){ Set<String> sensitiveWordSet = new HashSet<>(list); System.out.println("set: " + sensitiveWordSet); SensitiveWordUtil.init(sensitiveWordSet); System.out.println("初始化平台脏数据完成"); } System.out.println("初始化数据源未能获取到"); } boolean flag = false; for (int i = 0; i < txt.length(); i++) { int matchFlag = checkSensitiveWord(txt, i, matchType); //判断是否包含敏感字符 if (matchFlag > 0) { //大于0存在,返回true flag = true; } } return flag; } /** * 获取文字中的敏感词 * * @param txt 文字 * @return 敏感词 */ private static Set<String> getSensitiveWord(String txt, int matchType) { Set<String> sensitiveWordList = new HashSet<>(); for (int i = 0; i < txt.length(); i++) { int length = checkSensitiveWord(txt, i, matchType); //判断是否包含敏感字符 if (length > 0) { //存在,加入list中 sensitiveWordList.add(txt.substring(i, i + length)); i = i + length - 1; //减1的原因,是因为for会自增 } } return sensitiveWordList; } /** * 替换敏感字字符 * * @param txt 文本 * @param replaceStr 替换的字符串,匹配的敏感词以字符逐个替换,如 语句:你猜我是人么 敏感词:是人么,替换字符串:**,替换结果:你猜我*** * @return 敏感字字符结果 */ private static String replaceSensitiveWord(String txt, String replaceStr, int matchType) { String resultTxt = txt; Set<String> set = getSensitiveWord(txt, matchType); //获取所有的敏感词 Iterator<String> iterator = set.iterator(); String word; while (iterator.hasNext()) { word = iterator.next(); for (int i = 0; i < word.length() - 1; i++) { replaceStr += replaceStr; } resultTxt = resultTxt.replaceAll(word, replaceStr); } return resultTxt; } /** * 检查文字中是否包含敏感字符 * * @return 如果存在,则返回敏感词字符的长度,不存在返回0 */ private static int checkSensitiveWord(String txt, int beginIndex, int matchType) { boolean flag = false; //敏感词结束标识位:用于敏感词只有1位的情况 int matchFlag = 0; //匹配标识数默认为0 char word; Map nowMap = sensitiveWordMap; for (int i = beginIndex; i < txt.length(); i++) { word = txt.charAt(i); nowMap = (Map) nowMap.get(word); //获取指定key if (nowMap != null) { //存在,则判断是否为最后一个 matchFlag++; //找到相应key,匹配标识+1 if ("1".equals(nowMap.get("isEnd"))) { //如果为最后一个匹配规则,结束循环,返回匹配标识数 flag = true; //结束标志位为true if (SensitiveWordUtil.MinMatchTYpe == matchType) break; //最小规则,直接返回,最大规则还需继续查找 } } else break; //不存在,直接返回 } if (matchFlag < 2 || !flag) { //长度必须大于等于1,为词 matchFlag = 0; } return matchFlag; } /*public static void main(String[] args) { Set<String> sensitiveWordSet = new HashSet<>(); sensitiveWordSet.add("你妈"); sensitiveWordSet.add("有病"); //初始化敏感词库 SensitiveWordUtil.init(sensitiveWordSet); System.out.println("敏感词库的数量:" + SensitiveWordUtil.sensitiveWordMap.size()); String string = "我说你这孩子是不是有病,你妈的。"; System.out.println("准备检查的语句:" + string.length() + "个字"); //是否含有关键字 boolean result = SensitiveWordUtil.contains(string, SensitiveWordUtil.MaxMatchType); System.out.println("检查出了敏感词?" + result); //获取语句中的敏感词 Set<String> set = SensitiveWordUtil.getSensitiveWord(string, SensitiveWordUtil.MaxMatchType); System.out.println("语句中包含敏感词的个数为:" + set.size() + ",包含:" + set); //替换语句中的敏感词 String s = "*"; String filterStr = SensitiveWordUtil.replaceSensitiveWord(string, s, SensitiveWordUtil.MaxMatchType); System.out.println("001号已为你重新组织了你的语言:" + filterStr); }*/ }调用方如下:
private boolean verification(String title) { List<String> list = JSON.parseArray((String) redisTemplate.opsForValue().get("curse:list"), String.class); /* * 参数一:是我们要检验的字符串 * * 参数二:是我们过滤的规则 * * 参数三:是我们的脏词库(这里我直接从redis取出了) */ return SensitiveWordUtil.contains(title, SensitiveWordUtil.MaxMatchType, list); }作者:Teddy (公众号:鸡仓故事汇) ok!到这里就大功告成,小编(Teddy)在这里先感谢大家的到来。 虽然不是太详细,小编已经很努力,给小编来个一键三连(点赞,关注,收藏),小编会越来越努力。。。