前言

前段时间,公司的IM SDK想做敏感词过滤,但是后端的小伙伴《比较忙》,在开产品需求会的时候想把敏感词过滤放到前端,让iOS、安卓自己搞,但是前端小伙伴写了一个方法来检测一段文本,耗时【一两秒】钟而且比较耗CPU,这样肯定不行的,最后后端小伙伴妥协了,把敏感词过滤放到后端了。

一般的思路可能是遍历敏感词库,然后把一段文字的敏感词过滤掉,但是针对比较大的词库时(比如我们的敏感词库10万),这样非常耗时和耗内存,在电脑上还能跑跑,但是在手机上分分钟钟被系统杀死掉,这样肯定是不行的,这里就用到一种DFA算法。

但是使用了DFA算法,十万的敏感词库过滤一句话只需要【0.434510秒】!

2019-10-23 14:34:08.230918+0800 DFAFilterDemo[4728:4650502] message == 小明骂小王是个王八蛋,小王骂小明是个王八羔子!
2019-10-23 14:34:08.232972+0800 DFAFilterDemo[4728:4650502] result == 小明骂小王是个***,小王骂小明是个王八羔子!
2019-10-23 14:34:08.316380+0800 DFAFilterDemo[4728:4650502] 总共耗时: 0.434510

DFA算法

简介

何谓DFA,它的全称是Deterministic Finite Automaton,即确定有穷自动机;其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号;DFA算法的核心是建立了以敏感词为基础的许多敏感词树。

描述

我们先把敏感词库拆分解析成一个”敏感词树“,我们以敏感词”王八蛋“和”王八羔子“为例:

拆成的敏感词树如下:

代码

OC代码

//
//  DFAFilter.m
//  DFAFilterDemo
//
//  Created by 张福杰 on 2019/10/22.
//  Copyright © 2019 张福杰. All rights reserved.
//#import "DFAFilter.h"@interface DFAFilter ()@property (nonatomic,strong) NSMutableDictionary *keyword_chains;
@property (nonatomic,  copy) NSString *delimit;@end@implementation DFAFilter- (instancetype)init{if(self == [super init]){_delimit = @"\x00";}return self;
}/// 读取解析敏感词
- (void)parseSensitiveWords:(NSString *)path{if(path == nil){path = [[NSBundle mainBundle] pathForResource:@"sensitive_words" ofType:@"txt"];}NSString *content = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];NSArray *keyWordList = [content componentsSeparatedByString:@","];for (NSString *keyword in keyWordList) {[self addSensitiveWords:keyword];}NSLog(@"keyword_chains == %@",self.keyword_chains);
}- (void)addSensitiveWords:(NSString *)keyword{keyword = keyword.lowercaseString;keyword = [keyword stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];NSMutableDictionary *node = self.keyword_chains;for (int i = 0; i < keyword.length; i ++) {NSString *word = [keyword substringWithRange:NSMakeRange(i, 1)];if (node[word] == nil) {node[word] = [NSMutableDictionary dictionary];}node = node[word];}//敏感词最后一个字符标识[node setValue:@0 forKey:self.delimit];
}- (NSString *)filterSensitiveWords:(NSString *)message replaceKey:(NSString *)replaceKey{replaceKey = replaceKey == nil ? @"*" : replaceKey;message = message.lowercaseString;NSMutableArray *retArray = [[NSMutableArray alloc] init];NSInteger start = 0;while (start < message.length) {NSMutableDictionary *level = self.keyword_chains.mutableCopy;NSInteger step_ins = 0;NSString *message_chars = [message substringWithRange:NSMakeRange(start, message.length - start)];for(int i = 0; i < message_chars.length; i++){NSString *chars_i = [message_chars substringWithRange:NSMakeRange(i, 1)];if([level.allKeys containsObject:chars_i]){step_ins += 1;NSDictionary *level_char_dict = level[chars_i];if(![level_char_dict.allKeys containsObject:self.delimit]){level = level_char_dict.mutableCopy;}else{NSMutableString *ret_str = [[NSMutableString alloc] init];for(int i = 0; i < step_ins; i++){[ret_str appendString:replaceKey];}[retArray addObject:ret_str];start += (step_ins - 1);break;}}else{[retArray addObject:[NSString stringWithFormat:@"%C",[message characterAtIndex:start]]];break;}}start ++;}return [retArray componentsJoinedByString:@""];
}- (NSMutableDictionary *)keyword_chains{if(_keyword_chains == nil){_keyword_chains = [[NSMutableDictionary alloc] initWithDictionary:@{}];}return _keyword_chains;
}@end

Swift代码

//
//  DFAFilter.swift
//  DFAFilterDemo
//
//  Created by 张福杰 on 2019/10/23.
//  Copyright © 2019 张福杰. All rights reserved.
//import UIKitclass DFAFilter: NSObject {lazy var keyword_chains: NSMutableDictionary = {let dict = NSMutableDictionary()return dict}()lazy var delimit: String = "\\x00";/// 读取敏感词func parseSensitiveWords() -> Void {let path = Bundle.main.path(forResource: "sensitive_words", ofType: "txt");let url = URL(fileURLWithPath: path!)do {let data = try Data(contentsOf: url)let content: String = String(data: data, encoding: String.Encoding.utf8)!let keyWordList = content.components(separatedBy: ",")for keyword in keyWordList {addSensitiveWords(keyword)}} catch let error as Error? {print(error?.localizedDescription as Any)}}/// 添加敏感词到敏感词树func addSensitiveWords(_ keyword: String) -> Void {let keyword: String = keyword.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)var node = self.keyword_chainsif keyword.count <= 0{return}for index in 0...keyword.count - 1 {let index0 = keyword.index(keyword.startIndex, offsetBy: index)let index1 = keyword.index(keyword.startIndex, offsetBy: index + 1)let word = keyword[index0..<index1]if node[word] == nil{node[word] = NSMutableDictionary()}node = node[word] as! NSMutableDictionary}node[self.delimit] = 0}/// 开始过滤敏感词func filterSensitiveWords(_ message: String, replaceKey: String) -> String {let replaceKey = replaceKey.count > 0 ? replaceKey : "*"let message = message.lowercased()let retArray: NSMutableArray = NSMutableArray()var start = 0while start < message.count {var level: NSMutableDictionary = self.keyword_chains.mutableCopy() as! NSMutableDictionaryvar step_ins = 0let message_chars = getChar(message, startIndex: start, endIndex: message.count)for index in 0...message_chars.count - 1 {let chars_i = getChar(message_chars, startIndex: index, endIndex: index + 1)if level[chars_i] != nil{step_ins += 1let level_char_dict: NSDictionary = level[chars_i] as! NSDictionaryif level_char_dict[self.delimit] == nil{level = level_char_dict.mutableCopy() as! NSMutableDictionary}else{var ret_str = ""for _ in 0...step_ins - 1 {ret_str += replaceKey}retArray.add(ret_str)start += (step_ins - 1)break}}else{let word = getChar(message, startIndex: start, endIndex: start + 1)retArray.add(word)break}}start += 1}return retArray.componentsJoined(by: "")}func getChar(_ message: String, startIndex: NSInteger, endIndex: NSInteger) -> String {let index0 = message.index(message.startIndex, offsetBy: startIndex)let index1 = message.index(message.startIndex, offsetBy: endIndex)let word = message[index0..<index1]return String(word)}}

Python代码

# -*- coding: utf-8 -*-
# @Author: zhangfujie
# @Date:   2019/10/22
# @Last Modified by:   zhangfujie
# @Last Modified time: 2019/10/22
# @ ---------- DFA过滤算 ----------
import time
time1 = time.time()class DFAFilter(object):"""DFA过滤算法"""def __init__(self):super(DFAFilter, self).__init__()self.keyword_chains = {}self.delimit = '\x00'# 读取解析敏感词def parseSensitiveWords(self, path):ropen = open(path,'r')text = ropen.read()keyWordList = text.split(',')for keyword in keyWordList:self.addSensitiveWords(str(keyword).strip())# 生成敏感词树def addSensitiveWords(self, keyword):keyword = keyword.lower()chars = keyword.strip()if not chars:returnlevel = self.keyword_chainsfor i in range(len(chars)):if chars[i] in level:level = level[chars[i]]else:if not isinstance(level, dict):breakfor j in range(i, len(chars)):level[chars[j]] = {}last_level, last_char = level, chars[j]level = level[chars[j]]last_level[last_char] = {self.delimit: 0}breakif i == len(chars) - 1:level[self.delimit] = 0# 过滤敏感词def filterSensitiveWords(self, message, repl="*"):message = message.lower()ret = []start = 0while start < len(message):level = self.keyword_chainsstep_ins = 0message_chars = message[start:]for char in message_chars:if char in level:step_ins += 1if self.delimit not in level[char]:level = level[char]else:ret.append(repl * step_ins)start += step_ins - 1breakelse:ret.append(message[start])breakstart += 1return ''.join(ret)if __name__ == "__main__":gfw = DFAFilter()gfw.parseSensitiveWords('shieldwords.txt')text = "小明骂小王是个王八蛋,小王骂小明是个王八羔子!"result = gfw.filterSensitiveWords(text)print(result)time2 = time.time()print('总共耗时:' + str(time2 - time1) + 's')

结束语

demo下载地址: https://gitee.com/zfj1128/DFAFilterDemo
过往大佬喜欢的给个小星星吧!

欢迎各位大神提出宝贵的意见和建议,也欢迎大家进群交流365152048!

CSDN博客 https://zfj1128.blog.csdn.net
GITEE主页 https://gitee.com/zfj1128
GITHUB主页 https://github.com/zfjsyqk

算法-DFA算法-敏感词过滤算法(OC、Swift、Python)相关推荐

  1. 敏感词过滤算法 为内容保驾护航 Java/.Net/C++/c/Python等语言是如何进行敏感词打码限制的 高效防范违规内容

    有人的地方,就有江湖,有输入框的地方,就有注入风险!有输入框的地方,就有敏感词!敏感词就像一个平台杀手,可能直接导致平台被封锁! 敏感词是一个APP.一个网站.一个内容平台的"杀手" ...

  2. trie树之敏感词过滤算法

    之前写过一篇关于Trie树的介绍:Trie树--在一个字符串集合中快速查找某个字符串.今天就用Trie树来实现敏感词过滤算法. 首先简单介绍一下Trie树的数据结构: 1.根节点不存储字符. 2.Tr ...

  3. 【C++】实现敏感词过滤算法(含源码)

    敏感词过滤算法(聚合词树查询法) 1.构建词树 2.敏感词判断 3.遍历文本 关于敏感词过滤算法,数不胜数,在参考众多算法后,选取了比较实用的算法,进行总结与改进.大家可以参考一下链接: 敏感词过滤算 ...

  4. 浅析过滤敏感词过滤算法(C++)

    本文转自浅析敏感词过滤算法(C++),自己也在其基础上根据自己的情况做了一点修改. 为了提高查找效率,这里将敏感词用树形结构存储,每个节点有一个map成员,其映射关系为一个string对应一个Word ...

  5. dfa算法 java_java实现敏感词过滤(DFA算法)

    小Alan在最近的开发中遇到了敏感词过滤,便去网上查阅了很多敏感词过滤的资料,在这里也和大家分享一下自己的理解. 在写之前,小Alan给大家推荐一篇来自http://cmsblogs.com/?p=1 ...

  6. 基于DFA算法实现文章敏感词过滤

    最近公司要出一个论坛系统 因为最近貌似xxx查的也比较严,所以图片和文字安全一样要注意 其中文字就涉及到敏感字过滤的问题 目前大概流传两种解决办法: 1.利用分词器分词实现过滤 比如见得比较多的 IK ...

  7. 记录一次敏感词过滤算法DFA的应用案例

    目录 0. DFA是什么? 1.为什么要用DFA 2.DFA工具类实现 3.性能对比效果 3.1 普通关键字过滤 3.2 DFA关键字过滤 0. DFA是什么? 参考文档:敏感词过滤的算法原理之DFA ...

  8. DFA算法:简易Java敏感词过滤(含源文件和上万敏感词列表)

    敏感词过滤说白了就是简单的字符串替换,Java本身已经提供了相关函数,但是一旦遇到长文本,或者敏感词数量庞大,效率下降就会非常明显.本文将介绍利用多叉树进行敏感词存储和过滤的方法. 多叉树 多叉树是一 ...

  9. java实现敏感词过滤算法DFA并忽略敏感词中的特殊字符

    参考文章:https://blog.csdn.net/chenssy/article/details/26961957 补充说明: 1.具体的DFA介绍参考原文章,此处只是补充了文章中没有介绍的点以及 ...

最新文章

  1. Linux 有趣命令
  2. Python 2退出历史舞台 一句话证明它的重要性
  3. 期货市场计算机分析指南在线,期货市场计算机分析指南
  4. 敏捷开发之产品级经验分享
  5. Windows Azure 基本操作手册
  6. centos7 防火墙_【Linux简单实用小命令001】CentOS 7、8的防火墙端口开放
  7. 切图具体需要切什么内容_什么是切图?网页制作中的切图是什么?
  8. 你真的搞懂Class,class了么?
  9. Windows常见错误
  10. STM32输出PWM波形以及实现LED呼吸灯
  11. Docker的平行空间通信
  12. ubuntu基本操作命令超全(上)
  13. 神气蹦蹦 - 我原创可爱游戏
  14. 深度学习:Keras入门(一)之基础篇
  15. 在传染病中,肠道微生物-免疫力-营养在优化治疗策略中的作用
  16. AMBA总线协议(包含AHB与APB)
  17. 怎么推导亥姆霍兹方程
  18. 八数码问题可解性及扩展
  19. 酒店直播服务器系统,用ffmpeg+nginx服务器实现类似酒店视频直播系统
  20. 传统农业插上数字翅膀,种地变得更智慧

热门文章

  1. photoshop运用油漆桶工具填充图片局部颜色
  2. kindle touch 修复
  3. oracle停止job任务视图,【学习笔记】Oracle dba_datapump_jobs中not running作业的清除方法...
  4. DNSPod十问李开复:为什么我们对AI既期待又害怕?
  5. IDEA使用JDBC链接MySql数据库(使用工具类)案例转转好物购物平台
  6. 32岁,女,想转行做软件测试,不知道前景怎么样,学完会不会找不到工作?
  7. 微软将很快就要为Windows 10带来全新“画图”软件
  8. Use Case软件开发
  9. 36day 新浪微博(CELL时间 来源)
  10. 单根信号跨时钟域——两级D触发器消除亚稳态