• 敏感字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

  • 相关阅读:
    人月神话阅读笔记01
    梦断代码阅读笔记03
    构建之法阅读笔记03
    构建之法阅读笔记02
    个人课程总结
    第十六周进度总结
    计算最长英语单词链
    第十五周进度总结
    浪潮之巅阅读笔记03
    冲刺2-10
  • 原文地址:https://www.cnblogs.com/manusas/p/6757785.html
Copyright © 2020-2023  润新知