敏感词过滤说白了就是简单的字符串替换,Java本身已经提供了相关函数,但是一旦遇到长文本,或者敏感词数量庞大,效率下降就会非常明显。本文将介绍利用多叉树进行敏感词存储和过滤的方法。

多叉树

多叉树是一种特殊的数据结构,如下图

Head为头节点,下面的ABCDE均为子树。那么多叉树是如何存储敏感词的呢?首先将敏感词分解为一个一个的字符,例如敏感词"CSDN",第一个字符是C,则在Head下创建子树"C"(如果已经存在则跳过这一步)。第二个字符是S,则在子树"C"下面创建"S"接下来是D,N,创建完N就可以结束了。如此循环,就可以创建出类似上图的多叉树。

检测敏感词时,对于字符串中的每一个字符,先查找Head下是否有存在对应子树,例如字符串"ELN",先读取第一个字符E,并检查Head,发现存在子树"E";于是读取第二个字符L,并检查子树E的子树,发现存在L;最后读取第三个字符N,发现子树N还是存在。此时发现子树N后面没有内容,这说明ELN是一个敏感词,于是将ELN替换成"***"。

这种算法会出现一个小意外,如果一个敏感词恰好是另一个敏感词的前缀,就会导致较短的敏感词被长的敏感词覆盖,这种情况可以通过添加结束标记来区分。不过我的想法是,如果出现这种情况,直接把前缀屏蔽掉就行了,这样后半段也不算敏感词了(好像实际工作中不能这样做),因此我没有添加结束标记。

代码

首先要先写一个数据结构来模拟多叉树,下图里Word就是一颗树,里面保存着当前字符c和子树next,compareTo是用来排序的,以提高查找效率。

public class Word implements Comparable<Word>{public char c;public List next = null;public Word(char c){this.c = c;}@Overridepublic int compareTo(Word word) {return c - word.c;}
}

上图的List继承了ArrayList,主要是因为ArrayList可以动态添加元素,便于偷懒

import java.util.ArrayList;public class List extends ArrayList<Word> {public Word get(char c){for(Word w :this){if(w.c == c) return w;}return null;}/*** 二分查找,必须先升序排序* @param c 需要查找的字符* @return Word对象:如果找到   null:如果没找到*/public Word binaryGet(char c){int left,right,key;Word word;left = 0;right = this.size()-1;while (left <= right){key = (left + right) / 2;word = get(key);if(word.c == c){return word;}else if(word.c > c){right = key - 1;}else {left = key + 1;}}return null;}public Word add(char c){Word word = new Word(c);super.add(word);return word;}
}

以下是核心代码

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;public final class SensitiveWordFilter {public static List wordList;private final static char replace = '*'; // 替代字符private final static char[] skip = new char[]{ // 遇到这些字符就会跳过,例如,如果"AB"是敏感词,那么"A B","A=B"也会被屏蔽'!','*','-','+','_','=',',','.','@'};/*** 敏感词替换* @param text 待替换文本* @return 替换后的文本*/public static String Filter(String text){if(wordList == null || wordList.size() == 0) return text;char[] __char__ = text.toCharArray(); // 把String转化成char数组,便于遍历int i,j;Word word;boolean flag; // 是否需要替换for(i=0;i<__char__.length;i++){ // 遍历所有字符char c = __char__[i];word = wordList.binaryGet(c); // 使用二分查找来寻找字符,提高效率if(word != null){ // word != null说明找到了flag = false;j = i+1;while (j < __char__.length){ // 开始逐个比较后面的字符if(skip(__char__[j])) { // 跳过空格之类的无关字符j++;continue;}if(word.next != null){ // 字符串尚未结束,不确定是否存在敏感词/*以下代码并没有使用二分查找,因为以同一个字符开头的敏感词较少例如,wordList中记录了所有敏感词的开头第一个字,它的数量通常会有上千个假如现在锁定了字符“T”开头的敏感词,而“T”开头的敏感词只有10个,这时使用二分查找的效率反而低于顺序查找*/word = word.next.get(__char__[j]);if(word == null){break;}j++;}else { // 字符串已结束,存在敏感词汇flag = true;break;}}if(word != null && word.next == null){flag = true;}if(flag){ // 如果flag==true,说明检测出敏感粗,需要替换while (i<j){if(skip(__char__[i])){ // 跳过空格之类的无关字符,如果要把空格也替换成'*',则删除这个if语句i++;continue;}__char__[i] = replace;i++;}i--;}}}return new String(__char__);}/*** 加载敏感词列表* @param words 敏感词数组*/public static void loadWord(ArrayList<String> words){if(words == null) return;char[] chars;List now;Word word;wordList = new List();for(String __word__:words){if(__word__ == null) continue;chars = __word__.toCharArray();now = wordList;word = null;for(char c:chars){if(word != null) {if(word.next == null) word.next = new List();now = word.next;}word = now.get(c);if(word == null) word = now.add(c);}}sort(wordList);}/*** 加载敏感词txt文件,每个敏感词独占一行,不可出现空格,空行,逗号等非文字内容,必须使用UTF-8编码* @param path txt文件的绝对地址*/public static void loadWordFromFile(String path){String encoding = "UTF-8";File file = new File(path);try{if(file.isFile() && file.exists()){InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file),encoding);BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String line;ArrayList<String> list = new ArrayList<>();while ((line = bufferedReader.readLine()) != null){list.add(line);}bufferedReader.close();inputStreamReader.close();loadWord(list);}} catch (IOException e) {e.printStackTrace();}}/*** 对敏感词多叉树递增排序* @param list 待排序List*/private static void sort(List list){if(list == null) return;Collections.sort(list); // 递增排序for(Word word:list){sort(word.next);}}/*** 判断是否跳过当前字符* @param c 待检测字符* @return true:需要跳过   false:不需要跳过*/private static boolean skip(char c){for(char c1:skip){if(c1 == c) return true;}return false;}
}

测试

public class Main {public static void main(String[] args) throws Exception{SensitiveWordFilter.loadWordFromFile("D:/SensitiveWordList.txt");StringBuilder stringBuilder = new StringBuilder();long t1,t2;for(int i=0;i<100;i++){stringBuilder.append("123TM,D123");}String s = stringBuilder.toString();String result = null;t1 = System.nanoTime();for(int i=0;i<10000;i++){result = SensitiveWordFilter.Filter(s);}t2 = System.nanoTime();System.out.println(result);System.out.println((t2 - t1) / 1000000 + "毫秒");}
}

测试使用的敏感词库总共包含14596个敏感词(可能有个别重复),在测试代码里生成了一个长度为1000的字符串,总共包含100个相同敏感词,敏感词中间有逗号隔开

重复执行过滤10000次,并打印结果和时间,结果如下

可以看到程序成功地过滤了敏感词,并保留了逗号,总耗时335毫秒,平均每次过滤仅需要0.03毫秒,并且是在上万个敏感词和超长字符串的情况下。

源文件+敏感词列表

​​​​​​​敏感词库存在部分重复,不过不影响使用。

未加密链接

源代码+敏感词列表:SensitiveWord.zip - DearXuan的云盘

单独敏感词列表:SensitiveWordList.txt - DearXuan的云盘

蓝奏云加密链接

在寻找敏感词列表时发现很多人的分享都被取消了,为了防止敏感词列表被检测出敏感词,使用了zip格式并加密。

密码:dearxuan

源代码+敏感词列表:SensitiveWord.zip - 蓝奏云

单独敏感词列表:SensitiveWordList.zip - 蓝奏云

DFA算法:简易Java敏感词过滤(含源文件和上万敏感词列表)相关推荐

  1. 网站发帖敏感字java_网站敏感词过滤的实现(附敏感词库)

    现在基本上所有的网站都需要设置敏感词过滤,z似乎已经成了一个网站的标配,如果你的网站没有,或者你没有做相应的处理,那么小心相关部门请你喝茶哦. 最近在调研Java web网站的敏感词过滤的实现,网上找 ...

  2. 敏感词过滤的php代码,PHP敏感词过滤

    /** * 禁词过滤 * 执行效率:每篇用时0.05秒 * @author liuxu * */ class Logic_BlackWord { const APP_FORUM = 1; const ...

  3. 高斯模糊的Java实现及优化(含源文件)

    高斯模糊是被广泛使用的图形算法之一,在实现高斯模糊之前,先要了解正态分布 正态分布 一维的正态分布为 直接让f(x)和f(y)相乘,就得到了二维的正态分布 此处直接令μ=0,将会在下面解释. 权值矩阵 ...

  4. 基于java教学管理系统设计(含源文件)

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye 目录 1. 系统概述    1 1.1 系统目标    1 1.2 系统主要功能    1 2. ...

  5. 基于java的订餐系统设计(含源文件)

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye 基于Java的订餐系统设计与实现 客户端详细设计 1.设计分析 顾客模块的主要功能是浏览,订餐. ...

  6. 基于java会议管理系统设计(含源文件)

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye 一 绪论 1.1 本课题的开发背景及意义 当今社会竞争日益激烈,企事业单位内部会议也不断增多,会 ...

  7. 基于java员工管理系统设计(含源文件)

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye 需  求  文  档 软件名称:员工管理系统 一.概述:          在人才过剩的今天,企 ...

  8. 基于java物业管理信息系统设计(含源文件)

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye 基于B/S模式的物业管理信息系统设计 摘  要 随着市场经济的发展和人们生活水平的提高,大量的住 ...

  9. java dfa 敏感词_java利用DFA算法实现敏感词过滤功能

    前言 敏感词过滤应该是不用给大家过多的解释吧?讲白了就是你在项目中输入某些字(比如输入xxoo相关的文字时)时要能检 测出来,很多项目中都会有一个敏感词管理模块,在敏感词管理模块中你可以加入敏感词,然 ...

最新文章

  1. HTML 杨辉三角,杨辉三角 - 哼哼哈嘿 - OSCHINA - 中文开源技术交流社区
  2. python numba jit 的效率
  3. django(七)之数据库表的单表-增删改查QuerySet,双下划线
  4. 前端学习(1800):前端调试之清除浮动练习1
  5. ARM Cortex-M0 原理与应用实践
  6. Java数据结构与算法概述
  7. 【车标识别】基于SIFT算子的车标识别算法matlab仿真
  8. 史上最全的Linux常用命令汇总(超全面!超详细!)收藏这一篇就够了!
  9. libyuv 海思平台编译测试
  10. 计算机语言学习书籍目录资源自己找(到处复制粘贴的目录)
  11. 去除PDF文件中的斜体文字水印
  12. Realm学习(二)
  13. 极路由B70/极路由4增强版改spi,pb-boot启动后切换回nand刷nand breed
  14. php mysql弹幕_PHP开发弹幕系统
  15. python ipo模式包括什么_什么是IPO?
  16. android 10文件权限 三星,三星Android系统文件夹全解
  17. 金融无疆界 风险无极限——中国金融的未来之路
  18. 从管易云到金蝶云星空通过接口配置打通数据
  19. oracle查看表扩展,求助:如何查询oracle表空间允许自动扩展的最大值? - Oracle论坛 - 51CTO技术论坛_中国领先的IT技术社区...
  20. 音视频转码FFmpeg

热门文章

  1. Jmeter持续断言响应时间
  2. Git 分支与主干的合并
  3. cf1191 A. Tokitsukaze and Enhancement
  4. iis 安装织梦DedeCMS 教程
  5. 国王游戏(贪心算法+高精度运算)
  6. 分治法:线性时间选择
  7. HTML: css中的display属性
  8. golang环境 centos 7
  9. “刘畊宏女孩”背后的居家健身,市场潜力有多大?
  10. 【Linux】 mv剪切命令