关于java中敏感词检测的一些总结

本文介绍了一种基于敏感词库的简易敏感词检测算法,包括初始化敏感词库、生成敏感词索引以及检索敏感词的过程。算法通过将敏感词按长度分类存储,并利用索引数组提高检索效率。即使文本较长,也能快速检测是否存在敏感词。

之前项目里客户提出一个需求,需要对系统中使用文本转化成语音发送的功能进行敏感词检测,禁止用户提交有敏感词的语音。通过查询各方面资料,整理了大概几种方案:

  1. 项目启动时对载入敏感词库作为缓存(一个大map,敏感词为key,取任意值为value)。 对请求传入的文本分词,遍历分词结果,每个分词在map中查找,如果有值,则请求文本存在敏感词。
  2. 把敏感词库拼接成一个大的正则表达式,然后直接对文本匹配。
  3. 使用DFA(确定性有限状态自动机) DFA算法

对于方案选择,在网上参考了很多别人的代码。最简单的是方法2使用正则表达式,但是据说文本一长会有很大的效率问题。关于方法3DFA算法,由于在学校的时候算法课和编译原理没有认真听讲(惭愧= =||),直接就忽略这方法了,所以最后还是决定使用方法1。
其实方法1还是有很多可以改进的方法,后来又参考了这个帖子12楼中的方法,使用索引数组加关联数组的方式,提高了检索效率,甚至连分词的步骤都省掉了。整个实现代码如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import org.apache.commons.lang.StringUtils;
  
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
  
import java.io.IOException;
  
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
  
  
/**
* User: eternity
* Date: 2014/8/11
* Time: 16:17
* 敏感词检测类
* 敏感词检测初始化规则:
* 将敏感词从词库载入,按照2字、3字、4字、5字等字数各生成一个敏感词哈希表。
* 在将这些哈希表组成一个数组banWordsList,数组下标表示该敏感词表字数
* banWordsList[2] = {某马:true,屏蔽:true,啦啦:true};
* banWordsList[3] = {某个马:true,三个字:true,啦啦啦:true,小广告:true};
* banWordsList[4] = {某个坏银:true,四个字符:true,哈哈哈哈:true,就爱凤姐:true};
* banWordsList[5] = {某个大法好:true,五个敏感字:true};
* 根据上面几组组敏感词,自动生成以下索引
* 生成规则为,索引名是敏感词第一个字,值是一个int
* 该int的规则为,该int转换成二进制时,第i位为1表示上面4表存在长度为i的敏感词,否则不存在长度为i的敏感词(10000)
* wordIndex = {二:0x04,三:0x08,四:0x10,五:0x20,某:0x3c,啦:0x0c,哈:0x10,小:0x08,就:0x10};
*
* 检查规则如下:
* 1,逐字检验,是否该字在wordIndex索引表中。
* 2,如果不在表中,继续检验
* 3,如果在表中,根据索引表该键的值,取此字以及此字后的若干字检验详细表banWordsList[索引词长]。
*
* 检验例子
* 有一段如下文字,检验其是否包含敏感词:
“我就打小广告,气死版主”
——检测“我”
|-不在索引表
——检测“就”
|-在索引表
|-“就”的索引值是0x10,表示有4字以“就”开头的敏感词
|-取“就”和后面的字共4个,组成“就打小广”
|-查4字敏感词表,没有这项,继续
——检测“打”
|-不在索引表
——检测“小”
|-在索引表
|-索引值是0x08,表示有3字长度的敏感词
|-取“小”和“小”后面的字,共3个字组成一个词“小广告”
|-“小广告”在3字敏感词中,此帖包含敏感词,禁止发布
*/
public class BanWordsUtil {
     // public Logger logger = Logger.getLogger(this.getClass());
     public static final int WORDS_MAX_LENGTH = 10 ;
     public static final String BAN_WORDS_LIB_FILE_NAME = "banWords.txt" ;
  
     //敏感词列表
     public static Map[] banWordsList = null ;
  
     //敏感词索引
     public static Map<String, Integer> wordIndex = new HashMap<String, Integer>();
  
     /*
     * 初始化敏感词库
     */
     public static void initBanWordsList() throws IOException {
         if (banWordsList == null) {
             banWordsList = new Map[WORDS_MAX_LENGTH];
  
             for (int i = 0; i < banWordsList.length; i++) {
                 banWordsList[i] = new HashMap<String, String>();
             }
         }
  
         //敏感词词库所在目录,这里为txt文本,一个敏感词一行
         String path = BanWordsUtil.class.getClassLoader()
                                         .getResource(BAN_WORDS_LIB_FILE_NAME)
                                         .getPath();
         System.out.println(path);
  
         List<String> words = FileUtils.readLines(FileUtils.getFile(path));
  
         for (String w : words) {
             if (StringUtils.isNotBlank(w)) {
                 //将敏感词按长度存入map
                 banWordsList[w.length()].put(w.toLowerCase(), "");
  
                 Integer index = wordIndex.get(w.substring(0, 1));
  
                 //生成敏感词索引,存入map
                 if (index == null) {
                     index = 0;
                 }
  
                 int x = (int) Math.pow(2, w.length());
                 index = (index | x);
                 wordIndex.put(w.substring(0, 1), index);
             }
         }
     }
  
     /**
     * 检索敏感词
     * @param content
     * @return
     */
     public static List<String> searchBanWords(String content) {
         if (banWordsList == null ) {
             try {
                 initBanWordsList();
             } catch (IOException e) {
                 throw new RuntimeException(e);
             }
         }
  
         List<String> result = new ArrayList<String>();
  
         for ( int i = 0 ; i < content.length(); i++) {
             Integer index = wordIndex.get(content.substring(i, i + 1 ));
             int p = 0 ;
  
             while ((index != null ) && (index > 0 )) {
                 p++;
                 index = index >> 1 ;
  
                 String sub = "" ;
  
                 if ((i + p) < (content.length() - 1 )) {
                     sub = content.substring(i, i + p);
                 } else {
                     sub = content.substring(i);
                 }
  
                 if (((index % 2 ) == 1 ) && banWordsList[p].containsKey(sub)) {
                     result.add(content.substring(i, i + p));
  
                     // System.out.println("找到敏感词:"+content.substring(i,i+p));
                 }
             }
         }
  
         return result;
     }
  
     public static void main(String[] args) throws IOException {
         String content = "含有敏感词的测试语句。" ;
         BanWordsUtil.initBanWordsList();
         List<String> banWordList = BanWordsUtil.searchBanWords(content);
         for (String s : banWordLis){
          System.out.println( "找到敏感词:" +s);
         }
     }
}
上面测试语文本里面其实没有敏感词(我也怕被屏蔽XD),测试的时候随便加入几个敏感词都能检测出来的。这样就实现了一个简易又快速的敏感词检测,当然如果有需要比较复杂的检测逻辑(比如说“弹吉他妈妈真漂亮”这样的),还是要用到分词工具把词拆分一下的。

来自:http://my.oschina.net/u/1010578/blog/308904
### Java 中实现敏感词检测的方法 在 Java 中实现敏感词检测通常可以通过多种方式完成,其中最常见的是基于字典树(Trie Tree)的数据结构来提高匹配效率。以下是具体实现方法: #### 1. **字典树 (Trie Tree)** 的构建 字典树是一种高效的数据结构,用于存储字符串集合并支持快速查找操作。通过预先加载所有的敏感词到 Trie 树中,可以显著提升后续文本扫描的速度。 ```java import java.util.HashMap; import java.util.Map; public class TrieNode { private Map<Character, TrieNode> children = new HashMap<>(); private boolean isEndOfWord; public Map<Character, TrieNode> getChildren() { return children; } public void setEndOfWord(boolean endOfWord) { this.isEndOfWord = endOfWord; } public boolean isEndOfWord() { return isEndOfWord; } } ``` 上述代码定义了一个 `TrieNode` 类,它包含了子节点映射以及标记该节点是否是一个单词结尾的布尔值[^2]。 #### 2. **敏感词初始化** 将所有敏感词插入到 Trie 树中以便于后续检索。 ```java public class SensitiveWordsFilter { private final TrieNode root = new TrieNode(); public void addSensitiveWord(String word) { TrieNode current = root; for (char c : word.toCharArray()) { current = current.getChildren().computeIfAbsent(c, k -> new TrieNode()); } current.setEndOfWord(true); } // 更多逻辑... } ``` 此部分实现了向 Trie 结构添加单个敏感词的功能。每次调用 `addSensitiveWord()` 方法都会把一个新的敏感词加入到数据结构中[^3]。 #### 3. **文本扫描与替换** 对于给定的一段文字,逐字符遍历并与 Trie 树中的路径相比较,一旦发现存在完整的敏感词,则将其替换成指定符号或其他形式的内容。 ```java public String filter(String text, char replacementChar) { StringBuilder result = new StringBuilder(); int length = text.length(); TrieNode currentNode; for(int i=0; i<length;i++) { currentNode = root; int j=i; while(j < length && !currentNode.isEndOfWord()){ char ch=text.charAt(j); currentNode=currentNode.getChildren().get(ch); if(currentNode==null){ break; } ++j; } if(currentNode != null && currentNode.isEndOfWord()){ result.append(replacementChar).append(new String(new char[j-i-1]).replace('\0',replacementChar)); i=j-1; }else{ result.append(text.charAt(i)); } } return result.toString(); } ``` 这里展示了如何利用之前建立好的 Trie 来执行实际的文字过滤工作。如果找到任何匹配项,则会用特定字符代替原始内容;如果没有发现问题则保留原文本不变[^4]。 #### 总结说明 以上就是一种典型的基于 Trie 数据结构实现 Java 敏感词过滤器的方式。这种方法不仅能够有效处理大量关键词列表的情况,而且还能保持较高的查询性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值