java實現敏感詞過濾(DFA演算法)

来源:http://www.cnblogs.com/AlanLee/archive/2016/03/28/5329555.html
-Advertisement-
Play Games

小Alan在最近的開發中遇到了敏感詞過濾,便去網上查閱了很多敏感詞過濾的資料,在這裡也和大家分享一下自己的理解。 在寫之前,小Alan給大家推薦一篇來自http://cmsblogs.com/?p=1031的博文,也會參考部分內容來描述博文。 敏感詞過濾應該是不用給大家過多的解釋吧?講白了就是你在項 ...


小Alan在最近的開發中遇到了敏感詞過濾,便去網上查閱了很多敏感詞過濾的資料,在這裡也和大家分享一下自己的理解。

在寫之前,小Alan給大家推薦一篇來自http://cmsblogs.com/?p=1031的博文,也會參考部分內容來描述博文。

敏感詞過濾應該是不用給大家過多的解釋吧?講白了就是你在項目中輸入某些字(比如輸入xxoo相關的文字時)時要能檢測出來,很多項目中都會有一個敏感詞管理模塊,在敏感詞管理模塊中你可以加入敏感詞,然後根據加入的敏感詞去過濾輸入內容中的敏感詞併進行相應的處理,要麼提示,要麼高亮顯示,要麼直接替換成其它的文字或者符號代替。 

敏感詞過濾的做法有很多,我簡單描述我現在理解的幾種:

①查詢資料庫當中的敏感詞,迴圈每一個敏感詞,然後去輸入的文本中從頭到尾搜索一遍,看是否存在此敏感詞,有則做相應的處理,這種方式講白了就是找到一個處理一個。

優點:so easy。用java代碼實現基本沒什麼難度。

缺點:這效率讓我心中奔過十萬匹草泥馬,而且匹配的是不是有些蛋疼,如果是英文時你會發現一個很無語的事情,比如英文a是敏感詞,那我如果是一篇英文文檔,那程式它妹的得處理多少次敏感詞?誰能告訴我?

②傳說中的DFA演算法(有窮自動機),也正是我要給大家分享的,畢竟感覺比較通用,演算法的原理希望大家能夠自己去網上查查資料,這裡就不詳細說明瞭。

優點:至少比上面那sb效率高點。

缺點:對於學過演算法的應該不難,對於沒學過演算法的用起來也不難,就是理解起來有點gg疼,匹配效率也不高,比較耗費記憶體,敏感詞越多,記憶體占用的就越大。

③第三種在這裡要特別說明一下,那就是你自己去寫一個演算法吧,或者在現有的演算法的基礎上去優化,這也是小Alan追求的至高境界之一,如果哪位淫兄有自己的想法一定別忘了小Alan,可以加小Alan的QQ:810104041教小Alan兩招耍耍。

那麼,傳說中的DFA演算法是怎麼實現的呢?

第一步:敏感詞庫初始化(將敏感詞用DFA演算法的原理封裝到敏感詞庫中,敏感詞庫採用HashMap保存),代碼如下:

package com.cfwx.rox.web.sysmgr.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.cfwx.rox.web.common.model.entity.SensitiveWord;

/**
 * 敏感詞庫初始化
 * 
 * @author AlanLee
 *
 */
public class SensitiveWordInit
{
    /**
     * 敏感詞庫
     */
    public HashMap sensitiveWordMap;

    /**
     * 初始化敏感詞
     * 
     * @return
     */
    public Map initKeyWord(List<SensitiveWord> sensitiveWords)
    {
        try
        {
            // 從敏感詞集合對象中取出敏感詞並封裝到Set集合中
            Set<String> keyWordSet = new HashSet<String>();
            for (SensitiveWord s : sensitiveWords)
            {
                keyWordSet.add(s.getContent().trim());
            }
            // 將敏感詞庫加入到HashMap中
            addSensitiveWordToHashMap(keyWordSet);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return sensitiveWordMap;
    }

    /**
     * 封裝敏感詞庫
     * 
     * @param keyWordSet
     */
    @SuppressWarnings("rawtypes")
    private void addSensitiveWordToHashMap(Set<String> keyWordSet)
    {
        // 初始化HashMap對象並控制容器的大小
        sensitiveWordMap = new HashMap(keyWordSet.size());
        // 敏感詞
        String key = null;
        // 用來按照相應的格式保存敏感詞庫數據
        Map nowMap = null;
        // 用來輔助構建敏感詞庫
        Map<String, String> newWorMap = null;
        // 使用一個迭代器來迴圈敏感詞集合
        Iterator<String> iterator = keyWordSet.iterator();
        while (iterator.hasNext())
        {
            key = iterator.next();
            // 等於敏感詞庫,HashMap對象在記憶體中占用的是同一個地址,所以此nowMap對象的變化,sensitiveWordMap對象也會跟著改變
            nowMap = sensitiveWordMap;
            for (int i = 0; i < key.length(); i++)
            {
                // 截取敏感詞當中的字,在敏感詞庫中字為HashMap對象的Key鍵值
                char keyChar = key.charAt(i);

                // 判斷這個字是否存在於敏感詞庫中
                Object wordMap = nowMap.get(keyChar);
                if (wordMap != null)
                {
                    nowMap = (Map) wordMap;
                }
                else
                {
                    newWorMap = new HashMap<String, String>();
                    newWorMap.put("isEnd", "0");
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }

                // 如果該字是當前敏感詞的最後一個字,則標識為結尾字
                if (i == key.length() - 1)
                {
                    nowMap.put("isEnd", "1");
                }
                System.out.println("封裝敏感詞庫過程:"+sensitiveWordMap);
            }
            System.out.println("查看敏感詞庫數據:" + sensitiveWordMap);
        }
    }
}

第二步:寫一個敏感詞過濾工具類,裡面可以寫上自己需要的方法,代碼如下:

package com.cfwx.rox.web.sysmgr.util;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 敏感詞過濾工具類
 * 
 * @author AlanLee
 *
 */
public class SensitivewordEngine
{
    /**
     * 敏感詞庫
     */
    public static Map sensitiveWordMap = null;

    /**
     * 只過濾最小敏感詞
     */
    public static int minMatchTYpe = 1;

    /**
     * 過濾所有敏感詞
     */
    public static int maxMatchType = 2;

    /**
     * 敏感詞庫敏感詞數量
     * 
     * @return
     */
    public static int getWordSize()
    {
        if (SensitivewordEngine.sensitiveWordMap == null)
        {
            return 0;
        }
        return SensitivewordEngine.sensitiveWordMap.size();
    }

    /**
     * 是否包含敏感詞
     * 
     * @param txt
     * @param matchType
     * @return
     */
    public static boolean isContaintSensitiveWord(String txt, int matchType)
    {
        boolean flag = false;
        for (int i = 0; i < txt.length(); i++)
        {
            int matchFlag = checkSensitiveWord(txt, i, matchType);
            if (matchFlag > 0)
            {
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 獲取敏感詞內容
     * 
     * @param txt
     * @param matchType
     * @return 敏感詞內容
     */
    public static Set<String> getSensitiveWord(String txt, int matchType)
    {
        Set<String> sensitiveWordList = new HashSet<String>();

        for (int i = 0; i < txt.length(); i++)
        {
            int length = checkSensitiveWord(txt, i, matchType);
            if (length > 0)
            {
                // 將檢測出的敏感詞保存到集合中
                sensitiveWordList.add(txt.substring(i, i + length));
                i = i + length - 1;
            }
        }

        return sensitiveWordList;
    }

    /**
     * 替換敏感詞
     * 
     * @param txt
     * @param matchType
     * @param replaceChar
     * @return
     */
    public static String replaceSensitiveWord(String txt, int matchType, String replaceChar)
    {
        String resultTxt = txt;
        Set<String> set = getSensitiveWord(txt, matchType);
        Iterator<String> iterator = set.iterator();
        String word = null;
        String replaceString = null;
        while (iterator.hasNext())
        {
            word = iterator.next();
            replaceString = getReplaceChars(replaceChar, word.length());
            resultTxt = resultTxt.replaceAll(word, replaceString);
        }

        return resultTxt;
    }

    /**
     * 替換敏感詞內容
     * 
     * @param replaceChar
     * @param length
     * @return
     */
    private static String getReplaceChars(String replaceChar, int length)
    {
        String resultReplace = replaceChar;
        for (int i = 1; i < length; i++)
        {
            resultReplace += replaceChar;
        }

        return resultReplace;
    }

    /**
     * 檢查敏感詞數量
     * 
     * @param txt
     * @param beginIndex
     * @param matchType
     * @return
     */
    public static int checkSensitiveWord(String txt, int beginIndex, int matchType)
    {
        boolean flag = false;
        // 記錄敏感詞數量
        int matchFlag = 0;
        char word = 0;
        Map nowMap = SensitivewordEngine.sensitiveWordMap;
        for (int i = beginIndex; i < txt.length(); i++)
        {
            word = txt.charAt(i);
            // 判斷該字是否存在於敏感詞庫中
            nowMap = (Map) nowMap.get(word);
            if (nowMap != null)
            {
                matchFlag++;
                // 判斷是否是敏感詞的結尾字,如果是結尾字則判斷是否繼續檢測
                if ("1".equals(nowMap.get("isEnd")))
                {
                    flag = true;
                    // 判斷過濾類型,如果是小過濾則跳出迴圈,否則繼續迴圈
                    if (SensitivewordEngine.minMatchTYpe == matchType)
                    {
                        break;
                    }
                }
            }
            else
            {
                break;
            }
        }
        if (!flag)
        {
            matchFlag = 0;
        }
        return matchFlag;
    }

}

第三步:一切都準備就緒,當然是查詢好資料庫當中的敏感詞,並且開始過濾咯,代碼如下:

    @SuppressWarnings("rawtypes")
    @Override
    public Set<String> sensitiveWordFiltering(String text)
    {
        // 初始化敏感詞庫對象
        SensitiveWordInit sensitiveWordInit = new SensitiveWordInit();
        // 從資料庫中獲取敏感詞對象集合(調用的方法來自Dao層,此方法是service層的實現類)
        List<SensitiveWord> sensitiveWords = sensitiveWordDao.getSensitiveWordListAll();
        // 構建敏感詞庫
        Map sensitiveWordMap = sensitiveWordInit.initKeyWord(sensitiveWords);
        // 傳入SensitivewordEngine類中的敏感詞庫
        SensitivewordEngine.sensitiveWordMap = sensitiveWordMap;
        // 得到敏感詞有哪些,傳入2表示獲取所有敏感詞
        Set<String> set = SensitivewordEngine.getSensitiveWord(text, 2);
        return set;
    }

最後一步:在Controller層寫一個方法給前端請求,前端獲取到需要的數據併進行相應的處理,代碼如下:

    /**
     * 敏感詞過濾
     * 
     * @param text
     * @return
     */
    @RequestMapping(value = "/word/filter")
    @ResponseBody
    public RespVo sensitiveWordFiltering(String text)
    {
        RespVo respVo = new RespVo();
        try
        {
            Set<String> set = sensitiveWordService.sensitiveWordFiltering(text);
            respVo.setResult(set);
        }
        catch (Exception e)
        {
            throw new RoxException("過濾敏感詞出錯,請聯繫維護人員");
        }

        return respVo;
    }

  

小Alan在代碼中寫了不少的註釋,希望大家能夠動動自己的腦筋好好的理解一下。

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Session.Abandon();//清除全部Session//清除某個SessionSession["UserName"] = null;Session.Remove("UserName"); Session["aa"]=變數名; //賦值 ...
  • Julia中文教程資源.txt 2016年3月28日 05:18:32 codegay 本文更新在這裡: https://github.com/FGFW/julia science and technology 官方的資料總是少不了需要自己去看的. julia 英文手冊 http://docs.ju ...
  • 繼續上一章所講,上一章我們最後面說道,雖然這個是很小的程式,但還有好幾個要優化的地方。先複製一下老的view.py代碼。 其中驗證token的方法,已經重疊了,python教我們,永遠不要重覆自己的代碼,這是很醜陋的行為。今天我們把它換成一個裝飾器,然後再把redis調整一下,看看代碼會不會簡潔很多 ...
  • 最近折騰微信掃碼支付,看了微信官方文檔,找了很多網頁,發現和文檔/demo不匹配,現在自己算是弄出來了(文件名稱有所更改),貼出來分享一下 一.將有用的官方lib文件和使用的相關文件放置到vendor當中 二.調用同一支付介面 WechatController ...
  • 原址:http://blog.chedushi.com/archives/7258 最近在用 xlrd 寫一個題庫自動導出的程式,但碰到一個比較 ugly 的問題。 程式要求是將 xls 文件中的數據導出成文本,但 xlrd 在讀取數據時,會將 xls 單元格中所有可能是數字的數據都自動轉換成 py ...
  • 代碼: 中間就遇見一個問題很讓我糾結,就是xlrd 在讀取數據時,會將 xls 單元格中所有可能是數字的數據都自動轉換成 python 的 float。這時候,我們通過 str(cell.value) 會得到 12.0 (假設 cell.value = 12.0)。 解決辦法:比如我的a=1,那麼x ...
  • 2015老男孩Python培訓第八期視頻教程,希望您通過本教程的學習,能學會常用方法和技巧。教程從基礎知識開始講解一直到後期的案例實戰,完全零基礎學習,從初學者的角度探討分析問題,循序漸進由易到難,確保每一位初學者都能融會貫通。從零基礎到開發的大神,您只要兩個字——堅持!屌絲逆襲模式已經開啟,各位小 ...
  • 繼上一篇【Python數據分析】Python3操作Excel-以豆瓣圖書Top250為例 對豆瓣圖書Top250進行爬取以後,鑒於還有一些問題沒有解決,所以進行了進一步的交流討論,這期間得到了一隻尼瑪的幫助與啟發,十分感謝! 上次存在的問題如下: 1.寫入不能繼續的問題 2.在Python IDLE ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...