介绍
敏感词过滤是挺常见的需求,分享一种简易的实现
比如,小明和小红两个人因为某些不当行为,要求被全网封杀,所有小明需要被替换为**
准备
首先,需要准备一个敏感词列表
比如:
- 小明
- 小红
然后,需要一个高效的匹配算法
因为在实际生产中,敏感词数量会比较多,传入的文本也会比较长
单纯的遍历敏感词列表对字符串使用String.replace(key, "**")效果会比较差
这里使用了一种 Aho Corasick自动机结合DoubleArrayTrie极速多模式匹配
的算法来进行敏感词的匹配
实现
定义敏感词列表
private static final String[] SENSITIVE_KEYS = new String[]{
"小明",
"小红"
};
使用maven将算法库引用进来
<dependencies>
<dependency>
<groupId>com.hankcs</groupId>
<artifactId>aho-corasick-double-array-trie</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
使用匹配器来匹配敏感词位置并替换为’*’
public static String shadowSensitive(String text) {
StringBuffer sb = new StringBuffer(text);
// filter sensitive words
List<AhoCorasickDoubleArrayTrie.Hit<String>> hits = acdat.parseText(sb);
// shadow sensitive words
for (AhoCorasickDoubleArrayTrie.Hit<String> hit : hits) {
for (int i = hit.begin; i < hit.end; i++ ){
sb.deleteCharAt(i);
sb.insert(i, "*");
}
}
return sb.toString();
}
接下来测试一下,需要先初始化一下匹配器
public static void main(String[] args) { TreeMap<String, String> keys = new TreeMap<String, String>(); for (String key : SENSITIVE_KEYS) { keys.put(key, key); } acdat = new AhoCorasickDoubleArrayTrie<String>(); acdat.build(keys); String text1 = "小明上课吃零食,老师让小红出去"; String text2 = "小 明上课吃零食,老师让'小'红'出去"; System.out.println("text1:"); System.out.println(text1); System.out.println(shadowSensitive(text1)); System.out.println("text2:"); System.out.println(text2); System.out.println(shadowSensitive(text2)); }
执行程序后,控制台输出
text1: 小明上课吃零食,老师让小红出去 **上课吃零食,老师让**出去 text2: 小 明上课吃零食,老师让'小'红'出去 小 明上课吃零食,老师让'小'红'出去
可以看到text1中“小明”和“小红”已经被替换成了“**”
但是,在词语中简单的加入一些字符就可以绕开过滤器,这还需要优化一下
优化
匹配器只能匹配到“小明”,而无法匹配到“小 明”或者“小_明”
优化的思路如下:
- 输入的文本
小 明上课吃零食,老师让'小'红'出去
- 将一些常见的字符取出来,只留下文字内容
小明上课吃零食,老师让小红出去
- 进行敏感词的匹配,将敏感词改为*
**上课吃零食,老师让**出去
- 将取出的字符再重新插回去
* *上课吃零食,老师让'*'*'出去
参照这个思路,改写了上面的shadowSensitive方法
改线前需要定义一些常见的字符
private static final char[] SPECIAL_CHARS = new char[]{ ' ', '`', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=','+', '//', '|', '[', '{', ']', '}', ';', ':', '/'', '"', ',', '<', '.', '>', '/','?', //中文字符 ' ', '!', '¥', '…', '(', ')', '、', '「', '」', '【', '】', ';', ':', '“', '”', ',', '。', '《', '》', '?'};
为了方便展示,这里仅仅列举了常见的一部分字符,有需要的话,可以随时添加字符进去
优化的shadowSensitive方法:
public static String shadowSensitive(String text) { // detect special chars List<int[]> descriptors = new ArrayList<int[]>(); for (int i = 0; i < text.length(); i++) { for (int j = 0; j < SPECIAL_CHARS.length; j++) { if (text.charAt(i) == SPECIAL_CHARS[j]) { int[] descriptor = new int[2]; descriptor[0] = i; descriptor[1] = j; descriptors.add(descriptor); } } } // remove special chars StringBuffer sb = new StringBuffer(text); for (int i = descriptors.size() - 1; i >= 0; i--) { sb.deleteCharAt(descriptors.get(i)[0]); } // filter sensitive words List<AhoCorasickDoubleArrayTrie.Hit<String>> hits = acdat.parseText(sb); // shadow sensitive words for (AhoCorasickDoubleArrayTrie.Hit<String> hit : hits) { for (int i = hit.begin; i < hit.end; i++ ){ sb.deleteCharAt(i); sb.insert(i, "*"); } } // refill special chars for (int[] descriptor : descriptors) { sb.insert(descriptor[0], SPECIAL_CHARS[descriptor[1]]); } return sb.toString(); }
接下来测试一下
public static void main(String[] args) { TreeMap<String, String> keys = new TreeMap<String, String>(); for (String key : SENSITIVE_KEYS) { keys.put(key, key); } acdat = new AhoCorasickDoubleArrayTrie<String>(); acdat.build(keys); String text1 = "小明上课吃零食,老师让小红出去"; String text2 = "小 明上课吃零食,老师让'小'红'出去"; System.out.println("text1:"); System.out.println(text1); System.out.println(shadowSensitive(text1)); System.out.println("text2:"); System.out.println(text2); System.out.println(shadowSensitive(text2)); }
执行程序后,控制台输出:
text1: 小明上课吃零食,老师让小红出去 **上课吃零食,老师让**出去 text2: 小 明上课吃零食,老师让'小'红'出去 * *上课吃零食,老师让'*'*'出去
可以看到"小 明"已经修改为"
"了
原文
https://segmentfault.com/a/1190000020226308
本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 一种简易的敏感词过滤实现