过滤敏感词方式

时间:2022-10-12 15:20:43

一、利用正则表达式

关键正则表达式

.*(关键词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缓存这些数据,但我感觉是不是弄麻烦了)