落日楚天无际,凭栏目送飞鸿
序
现在很多项目都有发送短信的功能,我现在做的这个项目也不例外。在发送短信之前需要过滤掉敏感词,所以,在研究了网上的方法之后,我也写了这篇博客。下面分两个部分介绍敏感词过滤;
生成敏感词库
先介绍一下我们生成敏感词库的算法,叫DFA算法,即Deterministic Finite Automaton,也就是确定有穷自动机。也就是将每个敏感词生成树结构,然后将每棵树存入map集合,在查找敏感词时,通过调用map.get()方法查找。将敏感词生成树结构的代码:
public HashMap sensitiveWordMap;
/**
* @date 2019/10/18
* 参数keyWordSet是敏感词集合
* @description 读取敏感词库,将敏感词放入map中
*/
private void addSensitiveWordToHashMap(Set<String> keyWordSet) {
//初始化敏感词容器,减少扩容操作
sensitiveWordMap = new HashMap(keyWordSet.size());
String key = null;
Map nowMap = null;
Map<String, String> newWordMap = null;
//迭代keyWordSet
Iterator<String> iterator = keyWordSet.iterator();
while (iterator.hasNext()) {
//关键字
key = iterator.next();
//这里nowMap = sensitiveWordMap,是为了将两个map的引用地址指向同一个,以此,之后操作nowMap也就是相当于操作了sensitiveWordMap,因为它们指向同一个地址
nowMap = sensitiveWordMap;
for (int i = 0; i < key.length(); i++) {
//转换成char型
char keyChar = key.charAt(i);
Object wordMap = nowMap.get(keyChar);
if (wordMap != null) {
//如果存在该key,直接赋值
nowMap = (Map) wordMap;
} else {
//不存在,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
newWordMap = new HashMap<String, String>();
//不是最后一个
newWordMap.put("isEnd", "0");
nowMap.put(keyChar, newWordMap);
//重新给nowMap赋值的原因是:因为进入到此else时,已经确定当前字不在敏感词库中,那么他后面的字肯定也是不存在词库中的,
// 所以要给nowMap重新赋值,以便该字后面的字都进入该else,故加入敏感词库中
nowMap = newWordMap;
}
if (i == key.length() - 1) {
//最后一个
nowMap.put("isEnd", "1");
}
}
System.out.println(sensitiveWordMap);
}
}
通过上面的方法,就可以将敏感词重组成在map里的数结构,我这里做了个实验:
public static void main(String[] args) {
Set<String> keyWordSet = new HashSet<>();
keyWordSet.add("一个坏人");
keyWordSet.add("一个好人");
keyWordSet.add("中国人");
SensitiveWordInit init = new SensitiveWordInit();
init.addSensitiveWordToHashMap(keyWordSet);
}
结果如下,通过isEnd判断是否到了末级。
{
一 = {
个 = {
坏 = {
isEnd = 0,
人 = {
isEnd = 1
}
},
isEnd = 0,
好 = {
isEnd = 0,
人 = {
isEnd = 1
}
}
},
isEnd = 0
}, 中 = {
isEnd = 0,
国 = {
isEnd = 0,
人 = {
isEnd = 1
}
}
}
}
好,敏感词库的生成说完,接下来看看查找敏感词的逻辑。
查找敏感词
查找敏感词直接上代码:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @date 2019/10/18
* @description 敏感词过滤
*/
public class SensitivewordFilter {
private Map sensitiveWordMap = null;
/**
* @date 2019/10/18
* @description 获取文字中的敏感词
*/
public Set<String> getSensitiveWord(String txt) {
Set<String> sensitiveWordList = new HashSet<String>();
for (int i = 0; i < txt.length(); i++) {
//判断是否包含敏感字符
int length = checkSensitiveWord(txt, i);
//存在,加入list中
if (length > 0) {
sensitiveWordList.add(txt.substring(i, i + length));
//减1的原因,是因为for会自增
i = i + length - 1;
}
}
return sensitiveWordList;
}
/**
* @date 2019/10/18
* 参数中:txt为原文,replaceChar是替换敏感词字符
* @description 替换敏感字字符
*/
public String replaceSensitiveWord(String txt, String replaceChar) {
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(replaceChar, word.length());
resultTxt = resultTxt.replaceAll(word, replaceString);
}
return resultTxt;
}
/**
* @date 2019/10/18
* @description 获取替换字符串
*/
private String getReplaceChars(String replaceChar, int length) {
String resultReplace = replaceChar;
for (int i = 1; i < length; i++) {
resultReplace += replaceChar;
}
return resultReplace;
}
/**
* @date 2019/10/18
* @description 检查文字中是否包含敏感字符
*/
public int checkSensitiveWord(String txt, int beginIndex) {
//敏感词结束标识位:用于敏感词只有1位的情况
boolean flag = false;
//匹配标识数默认为0
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;
}
}
通过上面的replaceSensitiveWord方法,就可以将一段文字的敏感词替换为指定的字符。
这里我只做简单的介绍,具体词库和方法,我放百度云上了,请点击下载。
本文抛砖引玉,参考至:Java实现敏感词过滤