写项目时,得到一个新的需求,即实现敏感词的过滤,上网查了下,有几种实现方法,采取了DFA算法,即确定的有穷自动机算法,当初学习编译原理的时候为啥没想到DFA还能这么做,看来眼界和意识还是不够,得锻炼。
所谓的确定有限自动机算法,其实本质上是自上而下的文法分析中的一个步骤,即建立起一颗从上到下的语法树。
比如有如下敏感词:
大傻瓜,大傻瓜瓜逼,大傻子
建立起的语法树则是:
针对该情况,可以用HashMap来存贮语法树,当然,不同的敏感词开头则需要不同的语法树,下面列出敏感词转语法树的代码:
/**
* 将敏感关键词建立起多颗索引树,以HashMap存贮
* @param keyWordSet
* @return
*/
public void addKeyWordtoHashMap(Set<String> keyWordSet){
keyWordMap=new HashMap<String,String>();
String key = null;
Map nowMap = null;
Map<String, String> newWorMap = null;
Iterator<String> iterator = keyWordSet.iterator();
while(iterator.hasNext()) {
key = iterator.next();
nowMap = keyWordMap;
for (int i = 0; i < key.length(); i++) {
char keyChar = key.charAt(i);
Object wordMap = nowMap.get(keyChar);
if (wordMap != null) {
nowMap = (Map) wordMap;
} else {
newWorMap = new HashMap<String, String>();
//End为结束标志,1表示为该分支已完成,0表示该分支还未完成
newWorMap.put("End", "0");
nowMap.put(keyChar, newWorMap);
nowMap = newWorMap;
}
if (i == key.length() - 1) {
nowMap.put("End", "1");
}
}
}
}
敏感词可以存贮在数据库中,从中取出出来,存入集合中来建立起语法树,例如有以下敏感词:
[大傻逼, 傻子, 傻逼, 大傻子]
利用上述的代码建立起的HashMap有:
{大={End=0, 傻={子={End=1}, End=0, 逼={End=1}}}, 傻={子={End=1}, End=0, 逼={End=1}}}
接下来就是检测文本中是否含有敏感词了,检测敏感词的代码如下:
/**
* 检查从开始出的敏感词检测
* @param beginIndex
* @param text
* @return
*/
private int checkSensitiveWord(int beginIndex,String text){
Map newWordMap=keyWordMap;
int indexCount=0;//记录扫描敏感词而产生的偏移量
boolean isContainSensitiveWord=false;//用来记录是否检测到敏感词
char temp=0;
for (int i=beginIndex;i<text.length();i++){
temp=text.charAt(i);
//忽略无效字符
if (ignoreWord.get(temp)!=null){
indexCount++;
continue;
}
newWordMap=(Map)newWordMap.get(temp);
if (newWordMap!=null){
indexCount++;
if ("1".equals(newWordMap.get("End"))){
isContainSensitiveWord=true;
}
}else {
break;
}
}
//敏感词长度不能小于2
if (indexCount<2 || !isContainSensitiveWord){
indexCount=0;
}
//System.out.println(indexCount+"jjjj");
return indexCount;
}
ignoreWord
同样是一个HashMap,里面存贮着无效的字符,比如:
public static char[] IgnoreWords={'`','~','!','@','#','$','%','^','&','*','(',')',' ','.','。','·'};
获取文本中的敏感词以及替换:
/**
* 获得文中的敏感词汇
* @param text
* @return
*/
public Set<String> getSensitiveWord(String text){
Set<String> sensitiveWordList = new HashSet<String>();
for(int i = 0 ; i < text.length() ; i++){
int length = checkSensitiveWord(i,text);
if(length > 0){
//将敏感词给截取下来,存入集合中
sensitiveWordList.add(text.substring(i, i+length));
//text.substring(i,i+length);
i = i + length - 1;
}
}
return sensitiveWordList;
}
/**
* 指定字符串替换掉敏感词汇
* @param text
* @param replaceChar
* @return
*/
public String replaceSensitiveWord(String text,String replaceChar) throws IOException {
init();
String resultTxt = text;
Set<String> set = getSensitiveWord(text);
Iterator<String> iterator = set.iterator();
String word = null;
String replaceString = null;
while (iterator.hasNext()) {
word = iterator.next();
int index=resultTxt.indexOf(word);
resultTxt=resultTxt.substring(0,index)+replaceChar+resultTxt.substring(index+word.length());
}
return resultTxt;
}
从上述的代码可以看出DFA算法的时间复杂度是O(n2),加入有如下需验证的文本:
傻**逼**啦啦&&啦#傻子#啦啦啦大@ 傻#子
运行得出的结果为:
语句中包含的敏感词汇:3。包含[#傻子#, 大@ 傻#子, 傻**逼**]
[一两清风,半盏明月]啦啦&&啦[一两清风,半盏明月]啦啦啦[一两清风,半盏明月]