過濾敏感詞方式


一、利用正則表達式

關鍵正則表達式

.*(關鍵詞1|關鍵詞2|關鍵詞3).*

模擬業務代碼

@WebServlet(name = "PatternControl", urlPatterns = {"/p"})
public class PatternControl extends HttpServlet {
    private static final Pattern pattern = initPattern();

    private static Pattern initPattern() {
        List<String> stringList = null;
        try {
            stringList = Files.readAllLines(Paths.get("/Users/hans/Documents/word.txt"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        StringBuilder stringBuilder = new StringBuilder(".*(");
        stringBuilder.append(stringList.get(0));
        for (int i = 1; i < stringList.size(); i++) {
            stringBuilder.append("|" + stringList.get(i));
        }
        stringBuilder.append(").*");
        Pattern pattern = Pattern.compile(stringBuilder.toString());
        return pattern;
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (pattern == null) {
            response.sendError(500, "pattern is null");
            return;
        }
        if (request.getParameter("word") == null) {
            response.sendError(500, "word is null");
            return;
        }
        boolean isMatcher = pattern.matcher(request.getParameter("word")).matches();
        if (isMatcher) {
            response.sendError(209);
        } else {
            response.sendError(409);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

時間空間占用情況

前提

關鍵詞共有28448個,將其編譯成上述的正則表達式

CPU 2.2GHz Intel i7四核
內存

16GB 1600 MHz DDR3

時間情況(多次實驗平均結果)

階段 耗時(ms)
初始化

讀取敏感詞:38

編譯正則表達式:41

每次匹配 47

空間情況(多次實驗平均結果)

階段 消耗內存(MB)
初始化 編譯正則表達式 11
每次匹配 極小

 

cpu和堆運行時情況圖

結論 

利用正則表達式過濾敏感詞效果較好

 

二、利用字符串暴力匹配

核心思路

循環總共關鍵詞個數次,判斷時候在待匹配字符串中 是否包含 本次循環的關鍵詞

模擬業務代碼

@WebServlet(name = "PatternControl", urlPatterns = {"/p"})
public class PatternControl extends HttpServlet {
    private static final List<String> stringList = initStringList();

    private static List<String> initStringList() {
        List<String> stringList = null;
        try {
            stringList = Files.readAllLines(Paths.get("/Users/hans/Documents/word.txt"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stringList;
    }

    private boolean matchKeyWord(String text) {
        for (String markWord : stringList) {
            if (text.contains(markWord)) {
                return true;
            }
        }
        return false;
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (request.getParameter("word") == null) {
            response.sendError(500, "word is null");
            return;
        }
        boolean isMatcher = matchKeyWord(request.getParameter("word"));
        if (isMatcher) {
            response.sendError(209);
        } else {
            response.sendError(409);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

 

時間空間占用情況 

時間情況(多次實驗平均結果)

 

階段 耗時(ms)
初始化

讀取敏感詞:38

每次匹配 10

 

空間情況(多次實驗平均結果)

 

階段 消耗內存(MB)
初始化 3
每次匹配 極小

結論 

利用暴力匹配的效果更好

 

三、利用Tire樹匹配

核心思路

將所有的敏感詞構成一棵路徑樹,每個節點只有一個字符

模擬業務代碼

 

public class TestAcWithoutFail {
    public void insert(String str, Node root) {
        Node p = root;
        for (char c : str.toCharArray()) {
            if (p.childrenNodes.get(c) == null) {
                p.childrenNodes.put(c, new Node());
            }
            p = p.childrenNodes.get(c);
        }
        p.isEnd = true;
    }

    public boolean isContainSensitiveWord(String text, Node root) {
        for (int i = 0; i < text.length(); i++) {
            Node nowNode = root;
            for (int j = i; j < text.length(); j++) {
                char word = text.charAt(j);
                nowNode = nowNode.childrenNodes.get(word);
                if (nowNode != null) {
                    if (nowNode.isEnd) {
                        return true;
                    }
                } else {
                    break;
                }
            }
        }
        return false;
    }
    public String containSensitiveWord(String text, Node root) {
    		for (int i = 0; i < text.length(); i++) {
        		Node nowNode = root;
        		for (int j = i; j < text.length(); j++) {
            		char word = text.charAt(j);
            		nowNode = nowNode.childrenNodes.get(word);
            		if (nowNode != null) {
                		if (nowNode.isEnd) {
                    	return text.substring(i, j + 1);
                		}
            		} else {
                		break;
           		}
        	}
    	}
    	return "";
	}


    public static void main(String[] args) throws IOException, InterruptedException {
        List<String> stringList = Files.readAllLines(Paths.get("/Users/hans/Documents/word.txt"));
        TestAcWithoutFail acNoFail = new TestAcWithoutFail();
        Node root = new Node();
        for (String text : stringList) {
            acNoFail.insert(text, root);
        }
        String string = "tiresdfsdffffffffffaaaaaaaaaa";
        Thread.sleep(10 * 1000);
        for (int i = 0; i < 1; i++) {
            System.out.println(acNoFail.isContainSensitiveWord(string, root));
        }
    }
}

class Node {
    Map<Character, Node> childrenNodes = new HashMap<>();
    boolean isEnd;
}

時間空間占用情況 

時間情況(多次實驗平均結果) 

階段 耗時(ms)
初始化

讀取敏感詞:38

構建比較樹:36

每次匹配 0.01量級(執行1000遍34ms、執行10000遍130ms) 

空間情況(多次實驗平均結果)

 

階段 消耗內存(MB)
初始化

讀取字符串:3

構建比較樹:24

每次匹配 極小

結論 

在該業務中和暴力匹配效果哪個好值得商榷

四、服務部署方式

主站的做法(劉樂那邊的做法一樣)

  • 單獨部署一台服務器
  • 將關鍵詞放到一張表中
  • 設置定時任務每天初始化一次

我的方案

  • 將關鍵詞放到一張表中
  • 設置定時任務每天初始化一次(其實感覺每天更新都挺浪費的,寫個接口,表中數據改了之后,調用一下就可以)
  • 多台機器上每個機器都每天讀取一次數據庫,解決同步的問題(或者用Tair緩存這些數據,但我感覺是不是弄麻煩了)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM