Java 杂谈

DFA的敏感词过滤实现

2019-06-25  本文已影响1人  谁说咖啡不苦

DFA在wiki上面的解释是“确定有限状态自动机或确定有限自动机(deterministic finite automaton, DFA)是一个能实现状态转移的自动机”。这里我直白的一个自我理解就是通过一个事先维护好的状态集,自动进行一个判断转移到下一个状态。

状态转移

通过上面的图,我们来解释一个下大致的算法思路:

所以,大致的思路,就是通过输入的数据I([a,b,c,d])然后进行对Q状态集查询起始的状态,由I[0] = a开始作为一个入参去Q中寻找。假设找到了A1,然后,这个时候,我们的入参I还没操作完其他数据,这个时候,我们就要将状态转移到A1的下一个状态并且和我们的I[1]进行判断,看看是否能够成功的转移到A2上。

那么,通过上面的流程,我们把它引入到我们的敏感词的实现上:

具体的代码,我们通过Java的HashMap来进行一个实现:

  1. 先是一个构造我们的状态集合Q的代码:
  private static void init(Set<Sensitive> sensitiveWordSet) {

    Iterator<Sensitive> iterator = sensitiveWordSet.iterator();

    while (iterator.hasNext()) {
      Sensitive sensitive = iterator.next();

      char[] text = sensitive.getText().toCharArray();

      Map tempMap = hashMap;
      int index = 1;      // 标明是第几个文字,用于遮掩敏感词的时候,进行一个回溯.
      for (int i=0; i<text.length; i++) {
        char item = text[i];
        if (!tempMap.containsKey(item)) {
          DFANode dfaNode = new DFANode();
          dfaNode.setIndex(index);
          dfaNode.setSub(new HashMap());
          // 用于判断是否是最终文字
          dfaNode.setEnd(i == (text.length-1));     // end?

          tempMap.put(item, dfaNode);
          tempMap = dfaNode.getSub();
        } else {
          DFANode subNode = (DFANode) tempMap.get(item);
          tempMap = subNode.getSub();
        }
        index++;
      }
    }
  }

  private static void loadFile() throws IOException {
    File file = new File(
        DFATextFilter.class.getClassLoader().getResource("sensitive.txt").getFile());

    List<String> list = Files.readLines(file, Charset.forName("utf-8"));

    Set<Sensitive> sensitiveWordSet = new HashSet<>(40);

    list.forEach(item -> {
      sensitiveWordSet.add(Sensitive.builder().text(item).build());
    });

    init(sensitiveWordSet);
  }

@Data
@Builder
public class Sensitive {

  private String text;
}

@Data
public class DFANode {

  private boolean end;

  private int index;    // 标明单单在敏感字中的位置

  private Map sub;
}

这一部分的代码就是遍历我们的文件,然后逐行读取。

  1. 下面是一个过滤的代码:
  private  FilterResult check(String str, boolean mask) {
    Map tempCheckMap = hashMap;
    boolean exist = false;
    char[] chars = str.toCharArray();
    for (int i=0; i<chars.length; i++) {
      if (tempCheckMap.containsKey(chars[i])) {
        DFANode subNode = (DFANode) tempCheckMap.get(chars[i]);

        if (subNode.isEnd()) {
          // 遮掩敏感词
          if (mask) {
            int length = subNode.getIndex();
            for (int j=0; j<length; j++) {
              chars[i-j] = 'X';
            }
          }

          exist = true;
          str = String.valueOf(chars);
        } else {
          tempCheckMap = subNode.getSub();
        }
      } else {
//    从链路跳出从头开始,或者是第一步
        tempCheckMap = hashMap;
        if (tempCheckMap.containsKey(chars[i])) {
          DFANode subNode = (DFANode) tempCheckMap.get(chars[i]);

          if (subNode.isEnd()) {
            if (mask) {
              int length = subNode.getIndex();
              for (int j=0; j<length; j++) {
                chars[i-j] = 'X';
              }
            }
            exist = true;
            str = String.valueOf(chars);
            tempCheckMap = hashMap;
          } else {
            tempCheckMap = subNode.getSub();
          }
        }
      }
    }

    return FilterResult.builder().sensitive(exist).str(str).build();
  }

@Data
@ToString
@Builder
public class FilterResult {

  private String str;

  private boolean sensitive;
}

整体的代码实现还是比较简单,这里也就简单的提供一个思路。完整的代码查看链接: https://github.com/songshuangkk/DFA-Filter/tree/master

上一篇下一篇

猜你喜欢

热点阅读