敏感词过滤就是把文本中的违规词汇替换成“*”或者直接过滤。实际上有很多种实现方法
1.字符串匹配
通过Java的contains进行字符串匹配。使用这种的好处就是实现起来方便,但缺点就是文本和敏感词汇量大的时候它的效率是非常低的,并且是无法告诉我们具体的敏感词和下标。它只适合用于小规模文本或词汇量小的场景下
- public static boolean containsSensitiveWord(String text) {
- ReadTxtFiles readTxtFiles = new ReadTxtFiles();
- //获取敏感词文本词
- List<String> sensitiveWords = readTxtFiles.readTxtFiles(DAFFilter.class.getClassLoader().getResource("lexfiles").getFile());
- //进行循环判断
- for (String sensitiveWord : sensitiveWords) {
- if (text.contains(sensitiveWord)) {
- return true;
- }
- }
- return false;
- }
-
- public static void main(String[] args) {
- String text = "张三是个大xx";
- if (containsSensitiveWord(text)) {
- System.out.println("文本中存在敏感词");
- } else {
- System.out.println("文本中不存在敏感词");
- }
- }
2.前缀树
以下博主说的比较详细,就不过多阐述
3.DFA算法
DFA(Deterministic Finite Automaton,确定有限状态自动机)是一种用于字符串匹配和文本搜索的经典算法。特别适用于敏感词过滤
首先,为每个敏感词构建DFA。这个过程从一个初始状态开始,对于敏感词中的每个字符,如果当前状态下没有对应该字符的转换,则创建一个新的状态,并添加一条从当前状态到新状态的转换,这条转换由该字符触发。如果已经有了对应该字符的转换,则沿着这条转换到达下一个状态。重复这个过程直到敏感词的每个字符都被处理。最后一个字符对应的状态被标记为接受状态,意味着到达这个状态表示找到了一个敏感词。
假设我们的敏感词汇包含"abc","abd"。首先将这两个字符串构建不同的DFA:
- 初始状态0,对于输入c转到状态1,对于输入b转到状态4。
- 状态1,对于输入a转到状态2。
- 状态2,对于输入t转到状态3,状态3是接受状态,意味着找到了敏感词cat。
- 同理,状态4对于输入a转到状态5,状态5对于输入t转到状态6,状态6是接受状态,意味着找到了敏感词bat。
过滤文本:
当需要检测一段文本时,从DFA的初始状态和文本的第一个字符开始。对于文本中的每个字符,根据当前状态和字符确定下一个状态。如果存在对应字符的转换,则跟随这条转换到达下一个状态;如果不存在,则返回到初始状态。如果在某个点达到了接受状态,意味着匹配到了一个敏感词。然后可以选择标记或替换该敏感词,并继续处理文本的剩余部分。
现在,如果我们需要过滤一段文本:“我有一只狗叫:abc”,我们从初始状态0开始,遇到c转到状态1,接着a转到状态2,然后t转到状态3,此时达到接受状态,意味着我们发现了敏感词cat。根据需要,可以对这个敏感词进行标记或替换,然后继续处理文本的剩余部分。
- public class DAFFilter {
-
- // 构建敏感词的DFA
- private Set<String> sensitiveWords;
- private Map<Character, Object> dfa;
-
- public DAFFilter(Set<String> sensitiveWords) {
- this.sensitiveWords = sensitiveWords;
- buildDFA();
- }
-
- // 构建DFA
- private void buildDFA() {
- dfa = new HashMap<>();
- for (String word : sensitiveWords) {
- Map<Character, Object> current = dfa;
- for (int i = 0; i < word.length(); i++) {
- char c = word.charAt(i);
- if (!current.containsKey(c)) {
- current.put(c, new HashMap<Character, Object>());
- }
- if (i == word.length() - 1) {
- ((Map<Character, Object>) current.get(c)).put('!', '!'); // 使用一个特殊的字符表示敏感词的结尾
- } else {
- current = (Map<Character, Object>) current.get(c);
- }
- }
- }
- }
-
- // 过滤敏感词
- public String filter(String text) {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < text.length(); i++) {
- char c = text.charAt(i);
- Map<Character, Object> current = dfa;
- int j = i;
- while (current.containsKey(c)) {
- if (((Map<Character, Object>) current.get(c)).containsKey('!')) {
- for (int k = i; k <= j; k++) {
- result.append("*"); // 将敏感词替换为*
- }
- i = j; // 更新i
- break;
- } else {
- if (j == text.length() - 1) {
- break;
- }
- current = (Map<Character, Object>) current.get(c);
- c = text.charAt(++j);
- }
- }
- result.append(text.charAt(i)); // 添加未被替换的字符
- }
- return result.toString();
- }
-
- public static void main(String[] args) {
- ReadTxtFiles readTxtFiles = new ReadTxtFiles();
- //获取敏感词词
- Set<String> sensitiveWords =readTxtFiles.readTxtFiles(DAFFilter.class.getClassLoader().getResource("lexfiles").getFile()).stream().collect(Collectors.toSet());
- DAFFilter dafFilter = new DAFFilter(sensitiveWords);
- System.out.println(dafFilter.filter("张三是个大傻比"));
- }
- }
运行结果:
敏感词汇:
以上只是实现简单的敏感词过滤功能,如果需要可以扩展
推荐基于DFA算法实现的高性能Java敏感词过滤工具