敏感字DFA

DFA简介

在实现文字过滤的算法中,DFA是唯一比较好的实现算法。DFA即Deterministic Finite Automaton,也就是确定有穷自动机,它是是通过event和当前的state得到下一个state,即event+state=nextstate。下图展示了其状态的转换

         在这幅图中大写字母(S、U、V、Q)都是状态,小写字母a、b为动作。通过上图我们可以看到如下关系

a b b 
S -----> U S -----> V U -----> V

         在实现敏感词过滤的算法中,我们必须要减少运算,而DFA在DFA算法中几乎没有什么计算,有的只是状态的转换。

Java思路

 实现敏感词过滤的关键就是DFA算法的实现。首先我们对上图进行剖析。在这过程中我们认为下面这种结构会更加清晰明了。

         同时这里没有状态转换,没有动作,有的只是Query(查找)。我们可以认为,通过S query U、V,通过U query V、P,通过V query U P。通过这样的转变我们可以将状态的转换转变为使用Java集合的查找。

流程:

DFA算法典型运用:敏感词

敏感词:[中国, 法X轮X功, 中国人民] 备注:博客园也做了敏感词检测为了保存,实际情况为去掉中间的X的结果

 结构:(感觉就是树的遍历 isEnd是节点属性:判断是否关键字结束)

{中={isEnd=0, 国={人={民={isEnd=1}, isEnd=0}, isEnd=1}}, 法={isEnd=0, 轮={isEnd=0, 功={isEnd=1}}}}

{
 中={
    isEnd=0, 
    国={
       人={
          民={
             isEnd=1
             }, 
          isEnd=0
          },
       isEnd=1
       }
    }, 
 法={
    isEnd=0, 
    轮={
       isEnd=0, 
      功={
          isEnd=1
         }
       }
    }
}

例子 

工具类:

package com.wfz;

import java.io.*;
import java.util.*;

/**
 * Created by Liang on 5/5/2017.
 */
public class SensitiveWordUtils {
    public static int minMatchTYpe = 1;      //最小匹配规则
    public static int maxMatchType = 2;      //最大匹配规则
    private String ENCODE_GBK = "GBK";//编码模式

    /**
     * 文件读取敏感词汇
     *
     * @param file
     * @return 敏感词的set数组
     * @throws IOException
     */
    public Set readSensitiveWordFile(File file) throws IOException {
        Set<String> keyWordSet = null;
        BufferedReader reader = null;
        try {
            //读取文件
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), ENCODE_GBK));
            //文件存在性
            if (file.isFile() && file.exists()) {
                keyWordSet = new HashSet<String>();
                String keyword = null;
                ////读取文件,将文件内容放入到set中
                while ((keyword = reader.readLine()) != null) {
                    keyWordSet.add(keyword);
                }
            } else {
                throw new Exception("敏感词库文件不存在");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            reader.close();
        }
        return keyWordSet;
    }

    /**
     * 从Set中读取敏感字的hashmap,将敏感词加入到HashMap中,构建DFA算法模型
     *
     * @param keyWordSet
     */
    public HashMap getSensitiveWordHashMap(Set<String> keyWordSet) {
        HashMap result = new HashMap(keyWordSet.size());
        Map keymap = null;
        Map<String, String> endmap = null;
        for (String key : keyWordSet) {//得到关键字
            keymap = result;
            int len = key.length();
            for (int i = 0; i < len; i++) {
                char keychar = key.charAt(i);
                Object val = keymap.get(keychar);
                if (val != null) {//存在直接引用 定位keymap要定位的位置在哪个value下
                    keymap = (Map) val;
                } else {//不存在就赋值并添加
                    endmap = new HashMap<String, String>();
                    endmap.put("isEnd", "0");
                    keymap.put(keychar, endmap);
                    keymap = endmap;//一直保证尾部value添加map
                }
                if (i == len - 1) {//最后一个
                    keymap.put("isEnd", "1");
                }
            }
        }
        return result;
    }

    /**
     * 得到敏感词
     *
     * @param map       敏感词hashmap
     * @param txt       待匹配文字
     * @param matchType 匹配类型
     * @return
     */
    public Set<String> getSensitiveWord(Map map, String txt, int matchType) {
        Set<String> set = new HashSet<String>();
        for (int i = 0; i < txt.length(); i++) {
            int length = CheckSensitiveWord(map, txt, i, matchType);    //判断是否包含敏感字符
            if (length > 0) {    //存在,加入list中
                set.add(txt.substring(i, i + length));
                i = i + length - 1;    //减1的原因,是因为for会自增
            }
        }
        return set;
    }

    /**
     * 检查是否存在敏感词
     *
     * @param map        敏感词hashmap
     * @param txt        待匹配文字
     * @param beginIndex 文字索引
     * @param matchType  匹配类型
     * @return
     */
    private int CheckSensitiveWord(Map map, String txt, int beginIndex, int matchType) {
        boolean endflag = false;//isEnd标志位
        int lenflag = 0;//是否为词标志位 长度》1
        char word;
        int len = txt.length();
        for (int i = beginIndex; i < len; i++) {
            word = txt.charAt(i);
            map = (Map) map.get(word);
            if (map != null) {//字母匹配就循环内 再匹配 直到isEnd=1 长度和isEnd两个标志位判断是否匹配
                lenflag++;
                if ("1".equals(map.get("isEnd"))) {
                    endflag = true;
                    if (minMatchTYpe == matchType) {//最小规则,直接返回,最大规则还需继续查找
                        break;
                    }
                }
            } else {//不存在 直接返回
                break;
            }
        }
        if (lenflag < 2 || !endflag) {
            lenflag = 0;
        }
        return lenflag;
    }

    /**
     * 是否包含敏感词
     *
     * @param map       敏感词hashmap
     * @param txt       待匹配文字
     * @param matchType 匹配类型
     * @return
     */
    public boolean containSensitiveWord(Map map, String txt, int matchType) {
        boolean flag = false;
        for (int i = 0; i < txt.length(); i++) {
            int length = CheckSensitiveWord(map, txt, i, matchType);    //判断是否包含敏感字符
            if (length > 0) {    //存在,加入list中
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 替换敏感词
     *
     * @param map         敏感词hashmap
     * @param txt         待匹配文字
     * @param matchType   匹配类型
     * @param replaceChar 替换字符
     * @return
     */
    public String replaceSensitiveWord(Map map, String txt, int matchType, String replaceChar) {
        String ret = txt;
        Set<String> set = getSensitiveWord(map, txt, matchType);
        for (String word : set) {
            String replaceword = getReplaceChars(replaceChar, word.length());
            ret = ret.replaceAll(word, replaceword);
        }
        return ret;
    }

    /**
     * 得到替换的字符串
     *
     * @param replaceChar 替换字符
     * @param length      长度
     * @return
     */
    private String getReplaceChars(String replaceChar, int length) {
        String ret = replaceChar;
        for (int i = 1; i < length; i++) {
            ret += replaceChar;
        }
        return ret;
    }
}

测试类:

SensitiveWordUtils ins = new SensitiveWordUtils();
        File file = new File("D:\SensitiveWord.txt");
        Set set = ins.readSensitiveWordFile(file);
        System.out.println(set);
        HashMap map = ins.getSensitiveWordHashMap(set);
        System.out.println(map);
        String string = "爱看三.级.片的日本人民在中国旅游";
        System.out.println("待检测语句字数:" + string.length());
        long beginTime = System.currentTimeMillis();
        Set<String> ret = ins.getSensitiveWord(map, string, 1);
        long endTime = System.currentTimeMillis();
        System.out.println("语句中包含敏感词的个数为:" + ret.size() + "。包含:" + ret);
        boolean b = ins.containSensitiveWord(map, string, 1);
        System.out.println(b);
        String newstring = ins.replaceSensitiveWord(map, string, 1, "*");
        System.out.println(newstring);
        System.out.println("总共消耗时间为:" + (endTime - beginTime));
mail

结果:

[中国, 中国人民]
{中={isEnd=0, 国={人={民={isEnd=1}, isEnd=0}, isEnd=1}}}
待检测语句字数:17
语句中包含敏感词的个数为:1。包含:[中国]
true
爱看三.级.片的日本人民在**旅游
总共消耗时间为:0
mail

参考原文:hxxp://blog.csdn.net/chenssy/article/details/26961957

原文地址:https://www.cnblogs.com/manusas/p/6757785.html