Java使用DFA算法实现敏感词过滤
DFA,全称 Deterministic Finite Automaton 即确定有穷自动机。
其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号;DFA算法的核心是建立了以敏感词为基础的许多敏感词树。
对于一个敏感词来说,他把一个词拆分成一个个单独的字,对每一个字加上了一个状态,来区分是否是一个敏感词的末尾。来看一个例子:
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
|
|
配上dfa算法过滤敏感词的代码使用,帮助理解。
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
| package com.video.utils;
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component;
import java.io.*; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.Set;
@Component @Slf4j public class DFAUtil { private HashMap<String, Object> dfaMap=new HashMap<>();
private static HashMap<Boolean,String> result=new HashMap<>();
private static final int minMatchType=1;
private static final int maxMatchType=2;
public DFAUtil() throws IOException { begin(); }
public void createDFAHashMap(Set<String> set){ HashMap<String, Object> nowMap; for(String key:set){ nowMap=dfaMap; for(int i=0;i<key.length();i++){ String nowChar=String.valueOf(key.charAt(i)); HashMap<String, Object> map=(HashMap<String, Object>)nowMap.get(nowChar); if(map==null){ map=new HashMap<>(); nowMap.put(nowChar, map); } nowMap=map; if(nowMap.containsKey("isEnd") && nowMap.get("isEnd").equals("1")){ continue; } if(i!=key.length()-1){ nowMap.put("isEnd", "0"); }else{ nowMap.put("isEnd", "1"); } } } System.out.println(dfaMap); }
public HashMap<Boolean,String> getSensitiveWordByDFAMap(String string,int matchType){ StringBuilder stringBuilder=new StringBuilder(string); boolean on=false; for(int i=0;i<string.length();i++){ int length=getSensitiveLengthByDFAMap(string,i,matchType); if(length>0){ on=true; for (int j = 0; j < length; j++) { stringBuilder.setCharAt(i+j,'*'); } i=i+length-1; } } result.clear(); result.put(on,stringBuilder.toString()); return result; }
public int getSensitiveLengthByDFAMap(String string,int beginIndex,int matchType){ int nowLength=0; int resultLength=0; HashMap<String, Object> nowMap=dfaMap; for(int i=beginIndex;i<string.length();i++){ String nowChar=String.valueOf(string.charAt(i)); nowMap=(HashMap<String, Object>)nowMap.get(nowChar); if(nowMap==null){ break; }else{ nowLength++; if("1".equals(nowMap.get("isEnd"))){ resultLength=nowLength; if(matchType==minMatchType){ break; } } } } return resultLength; }
public void begin() throws IOException { Set<String> set=new HashSet<>(); BufferedReader br=null; try { String path= System.getProperty("user.dir") + "/word.txt"; br = new BufferedReader (new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8)); String s; while ((s = br.readLine()) != null) { set.add(s); } createDFAHashMap(set); log.info("初始化敏感词文件成功"); }catch (Exception e){ log.info("初始化敏感词文件出错"); }finally { if(br!=null) br.close(); } } }
|
这个我是用的txt文件来存储敏感词,这其中有一个大坑,就是我配置好文件的路径后,在本地运行是可以正常运行的(使用的相对路径),但是部署到服务器上却报错,显示找不到文件。应该是jar包中的文件路径跟本地的文件路径有差别,具体是什么原因我还是很困惑,于是我就使用了
1
| String path= System.getProperty("user.dir") + "/word.txt";
|
其中System.getProperty(“user.dir”)可以获取到jar包所在的当前目录。我是把存储敏感词的文件跟jar包放在了同一个文件夹下,这样可以正确的读取文件,但是这样还需要另发送一个word.txt的文件,所以不是最好的方法,之后再对这个进行改进吧。