当前位置: 首页 > news >正文

Java怎么实现一个敏感词过滤?有哪些方法?怎么优化?

敏感词过滤是非常常见的一种手段,避免出现一些违规词汇。

Java实现敏感词过滤的完整方案与优化策略

敏感词过滤是内容安全的重要组成部分,以下是Java中实现敏感词过滤的多种方法及其优化方案。

一、基础实现方法

1. 简单字符串匹配(适合小规模场景)

public class SimpleFilter {private static final Set<String> sensitiveWords = new HashSet<>(Arrays.asList("敏感词1", "敏感词2"));public static String filter(String text) {for (String word : sensitiveWords) {if (text.contains(word)) {text = text.replace(word, "***");}}return text;}
}

缺点:时间复杂度O(n*m),性能差,无法处理变形词,拼音等扩展功能。

2. 正则表达式匹配

public class RegexFilter {private static final String pattern = "敏感词1|敏感词2|敏感词3";public static String filter(String text) {return text.replaceAll(pattern, "***");}
}

缺点:正则构建时间长,敏感词多时性能下降明显。敏感词有些场景还是可以考虑的,可以做一个分片处理。

二、高效实现方案

1. Trie树(前缀树)实现

其实也就是一种树形有向图(无环结构。是DFA的一种特例(树形结构,无失败转移)。

class TrieNode {private Map<Character, TrieNode> children = new HashMap<>();private boolean isEnd;// 添加子节点方法// 查找子节点方法// getter/setter
}public class TrieFilter {private TrieNode root = new TrieNode();// 构建Trie树public void addWord(String word) {TrieNode node = root;for (char c : word.toCharArray()) {node = node.getChildren().computeIfAbsent(c, k -> new TrieNode());}node.setEnd(true);}// 过滤方法public String filter(String text) {StringBuilder result = new StringBuilder();TrieNode temp;for (int i = 0; i < text.length(); i++) {temp = root;int j = i;while (j < text.length() && temp.getChildren().containsKey(text.charAt(j))) {temp = temp.getChildren().get(text.charAt(j));j++;if (temp.isEnd()) {// 发现敏感词,替换为*result.append("*".repeat(j - i));i = j - 1;break;}}if (i >= text.length()) break;if (!temp.isEnd()) {result.append(text.charAt(i));}}return result.toString();}
}

优点:时间复杂度O(n),适合大规模敏感词库

前缀树的优点是,插入和查询效率高,特别是在敏感词有共同前缀的情况下(如ab、abc、abcd)。而且他的空间效率较高,因为是共享公共前缀的。

但是他也有缺点,一方面是构建树的初期成本较高。另外对于没有共同前缀的敏感词,效率提升不明显。

所以,前缀树适合做高效的字典查找、根据前缀自动补全、利用前缀匹配进行快速路由等场景。

2. DFA(确定性有限自动机)算法

DFA是Deterministic Finite Automaton的缩写,翻译过来叫确定有限自动机,DFA算法是一种高效的文本匹配算法,特别适合于敏感词过滤。

DFA由一组状态组成,以及在这些状态之间的转换,这些转换由输入字符串驱动。每个状态都知道下一个字符的到来应该转移到哪个状态。如果输入字符串结束时,DFA处于接受状态,则输入字符串被认为是匹配的。

其实就是一种一般有向图(可能含环,如自环)结构,满足一条路径则算匹配成功,就算一个敏感词了。

有三个参数组成

  1. 节点(States):表示自动机的状态,包括:

    • 初始状态(起点)

    • 中间状态

    • 终止状态(敏感词匹配成功的状态)

  2. 边(Transitions):表示状态之间的转移条件,每个边对应一个输入字符(如字母、汉字)。

  3. 终止状态:某些节点被标记为终止状态,代表从初始状态到该状态的路径对应一个完整的敏感词。

具体过程就像下面这样

  • 输入字符 c,检查当前状态是否有 c 对应的边。

  • 如果有,转移到下一个状态;如果没有,匹配失败。

  • 如果最终停在终止状态,则输入文本包含敏感词。

  • 否则,不包含。

public class DFAFilter {private Map<String, Object> sensitiveWordMap = new HashMap<>();// 构建敏感词库public void init(Set<String> words) {for (String word : words) {Map<String, Object> nowMap = sensitiveWordMap;for (int i = 0; i < word.length(); i++) {String key = String.valueOf(word.charAt(i));Object tempMap = nowMap.get(key);if (tempMap == null) {Map<String, Object> newMap = new HashMap<>();newMap.put("isEnd", "0");nowMap.put(key, newMap);nowMap = newMap;} else {nowMap = (Map<String, Object>) tempMap;}if (i == word.length() - 1) {nowMap.put("isEnd", "1");}}}}// 过滤方法public String filter(String text) {StringBuilder result = new StringBuilder();for (int i = 0; i < text.length(); i++) {int length = checkWord(text, i);if (length > 0) {result.append("*".repeat(length));i += length - 1;} else {result.append(text.charAt(i));}}return result.toString();}private int checkWord(String text, int beginIndex) {boolean flag = false;int matchLength = 0;Map<String, Object> tempMap = sensitiveWordMap;for (int i = beginIndex; i < text.length(); i++) {String word = String.valueOf(text.charAt(i));tempMap = (Map<String, Object>) tempMap.get(word);if (tempMap == null) break;matchLength++;if ("1".equals(tempMap.get("isEnd"))) {flag = true;break;}}return flag ? matchLength : 0;}
}
  1. 内存优化

    • 双数组Trie:压缩状态存储,减少内存占用。

    • 共享前缀:DFA合并相同前缀的状态(如 "敏感词" 和 "敏感内容" 共享 "敏感" 路径)。

  2. 匹配加速

    • AC自动机在DFA基础上添加失败指针,支持多模式匹配(类似KMP算法)。

    • 批处理:对长文本分块并行检测。

  3. 工程实践

    • 热更新:动态加载敏感词库,无需重启服务。

    • 多级过滤:先布隆过滤器快速排除无敏感词文本,再走DFA精确匹配。

给大家推荐一个基于 DFA 算法实现的高性能 java 敏感词过滤工具框架——sensitive-word

三、高级优化方案

1. 多模式匹配算法优化

AC自动机(Aho-Corasick算法)
public class ACFilter {private ACTrie trie;public void init(Set<String> words) {trie = new ACTrie();for (String word : words) {trie.insert(word);}trie.buildFailureLinks();}public String filter(String text) {Set<ACTrie.Match> matches = trie.parseText(text);char[] chars = text.toCharArray();for (ACTrie.Match match : matches) {Arrays.fill(chars, match.getStart(), match.getEnd() + 1, '*');}return new String(chars);}
}

优点:一次扫描匹配所有模式串,时间复杂度O(n)

2. 基于布隆过滤器的预处理

public class BloomFilterPreprocessor {private BloomFilter<String> bloomFilter;private Set<String> exactMatchSet;public void init(Set<String> words) {bloomFilter = BloomFilter.create(Funnels.stringFunnel(), words.size(), 0.01);exactMatchSet = new HashSet<>(words);words.forEach(bloomFilter::put);}public boolean mightContain(String text) {return bloomFilter.mightContain(text);}public boolean exactMatch(String text) {return exactMatchSet.contains(text);}
}

用途:先快速判断是否可能包含敏感词,再进行精确匹配

四、工程化实践方案

1. 敏感词库动态加载

public class DynamicWordFilter {private volatile Map<String, Object> wordMap;private ScheduledExecutorService executor;public void init() {loadWords();executor = Executors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(this::loadWords, 1, 1, TimeUnit.HOURS);}private void loadWords() {Map<String, Object> newMap = new HashMap<>();// 从数据库或文件加载敏感词Set<String> words = loadFromDB();// 构建DFA结构this.wordMap = buildDFA(words);}
}

2. 分布式敏感词过滤

public class DistributedFilter {private RedisTemplate<String, String> redisTemplate;public boolean isSensitive(String text) {// 使用Redis的Set结构存储敏感词return redisTemplate.opsForSet().isMember("sensitive:words", text);}public String filter(String text) {// 调用分布式过滤服务return restTemplate.postForObject("http://filter-service/filter", text, String.class);}
}

3:也可以考虑使用ElasticSearch做搜索引擎

为什么可以使用ES可以看看

【2025最新】为什么用ElasticSearch?和传统数据库MySQL与什么区别?-CSDN博客

总的来说就是ES有强大的文本分析查询能力来实现。以下是详细实现过程和方案:

具体实现方案
方案1:索引时敏感词标记(推荐)

步骤

  1. 自定义分析器

    PUT /sensitive_content_index
    {"settings": {"analysis": {"analyzer": {"sensitive_filter_analyzer": {"type": "custom","tokenizer": "standard","filter": ["lowercase","sensitive_word_filter"]}},"filter": {"sensitive_word_filter": {"type": "stop","stopwords": ["敏感词1", "敏感词2", "违法词"]}}}},"mappings": {"properties": {"content": {"type": "text","analyzer": "sensitive_filter_analyzer","fields": {"original": { "type": "keyword" // 保留原始内容}}}}}
    }
  2. 检测敏感词

    GET /sensitive_content_index/_analyze
    {"analyzer": "sensitive_filter_analyzer","text": "这是一段包含敏感词1的文本"
    }

    输出:敏感词会被过滤掉,只返回普通词项

  3. 写入时自动标记

    POST /sensitive_content_index/_doc
    {"content": "这是需要检测的文本","has_sensitive": false // 由pipeline更新
    }
  4. 使用Ingest Pipeline自动检测

    PUT _ingest/pipeline/sensitive_check_pipeline
    {"processors": [{"script": {"source": """def sensitiveWords = ['敏感词1', '违禁词'];for (word in sensitiveWords) {if (ctx.content.contains(word)) {ctx.has_sensitive = true;ctx.sensitive_word = word;break;}}"""}}]
    }

方案2:查询时敏感词过滤

使用Term查询检测

GET /content_index/_search
{"query": {"bool": {"must_not": [{ "terms": { "content": ["敏感词1", "违禁词"] }}]}}
}

高亮显示敏感词

GET /content_index/_search
{"query": {"match": { "content": "正常文本" }},"highlight": {"fields": {"content": {"highlight_query": {"terms": { "content": ["敏感词1", "违禁词"] }}}}}
}

方案3:结合机器学习(ES 7.15+)
  1. 训练敏感词分类模型

    PUT _ml/trained_models/sensitive_words_classifier
    {"input": {"field_names": ["text"]},"inference_config": {"text_classification": {"vocabulary": ["敏感词1", "变体词", "拼音词"]}}
    }
  2. 部署推理处理器

    PUT _ingest/pipeline/ml_sensitive_detection
    {"processors": [{"inference": {"model_id": "sensitive_words_classifier","field_map": { "content": "text" }}}]
    }

性能优化技巧
  1. 敏感词库存储优化

    • 使用ES的Synonyms Token Filter管理同义词/变体词

    • 将敏感词库存储在单独索引中,定期更新

  2. 缓存加速

    PUT /sensitive_words_cache
    {"mappings": {"properties": {"word": { "type": "keyword" }}}
    }
  3. 分布式检测

    • 对大型文档分片处理

    • 使用_search_shardsAPI并行检测

五、性能优化技巧

  1. 内存优化

    • 使用基本类型替代包装类

    • 压缩Trie树结构(Ternary Search Tree)

    • 对象复用减少GC压力

  2. 算法优化

    • 对短文本使用快速失败策略

    • 实现多级过滤(先粗筛后精筛),

    • 并行化处理(Fork/Join框架)

  3. 预处理优化

    • 文本归一化(全角转半角,繁体转简体)

    • 拼音转换处理(如"taobao"->"淘宝")

    • 近音词/形近词处理

  4. 缓存优化

    • 缓存常见文本的过滤结果

    • 使用Caffeine实现本地缓存

    • 布隆过滤器预判

六、完整生产级实现示例

public class ProductionWordFilter implements InitializingBean {private final TrieNode root = new TrieNode();private final List<String> wordSources;private final ScheduledExecutorService executor;public ProductionWordFilter(List<String> wordSources) {this.wordSources = wordSources;this.executor = Executors.newSingleThreadScheduledExecutor();}@Overridepublic void afterPropertiesSet() {reload();executor.scheduleWithFixedDelay(this::reload, 1, 1, TimeUnit.HOURS);}public synchronized void reload() {TrieNode newRoot = new TrieNode();wordSources.stream().flatMap(source -> loadWords(source).stream()).forEach(word -> addWord(newRoot, word));this.root = newRoot;}public FilterResult filter(String text) {StringBuilder result = new StringBuilder();Set<String> foundWords = new HashSet<>();int replacedCount = 0;for (int i = 0; i < text.length(); ) {MatchResult match = findNextMatch(text, i);if (match != null) {foundWords.add(match.getWord());result.append("*".repeat(match.getLength()));replacedCount++;i = match.getEndIndex();} else {result.append(text.charAt(i));i++;}}return new FilterResult(result.toString(), foundWords, replacedCount);}// 其他辅助方法...
}

七、评估指标

  1. 性能指标

    • 吞吐量(QPS)

    • 平均延迟(ms)

    • 99线延迟(ms)

  2. 效果指标

    • 召回率(漏判率)

    • 准确率(误判率)

    • 覆盖度(变形词识别率)

  3. 资源消耗

    • 内存占用

    • CPU使用率

    • 网络IO(分布式场景)

八、扩展思考

  1. 中文分词集成:结合IK Analyzer等分词工具处理更复杂的语义

  2. 机器学习模型:使用NLP模型识别变体、谐音、拆字等高级变种

  3. 图片/语音过滤:扩展多媒体内容过滤能力

  4. 多语言支持:处理Unicode混淆和国际化敏感词

对于大多数Java应用,Trie树或DFA算法配合定期更新的词库已经能够满足需求。

http://www.xdnf.cn/news/254359.html

相关文章:

  • 纹理对象创建
  • Nacos使用
  • 组件通信-props
  • 类与对象(中)
  • OnlyOffice Document Server 源码调试指南-ARM和x86双模式安装支持
  • < 自用文 Texas style Smoker > 美式德克萨斯烟熏炉 从设计到实现 (第一部分:烹饪室与燃烧室)
  • 类与类之间的关系详解
  • 部署Superset BI(二)再战Superset
  • 【信息系统项目管理师-论文真题】2013上半年论文详解(包括解题思路和写作要点)
  • AI编译器对比:TVM vs MLIR vs Triton在大模型部署中的工程选择
  • PyQt 或 PySide6 进行 GUI 开发文档与教程
  • 【东枫电子】AMD / Xilinx Alveo™ UL3422 加速器
  • MTV-SCA:基于多试向量的正弦余弦算法
  • GNOME扩展:ArcMenu的Brisk布局左右调换
  • 在Kali Linux上安装GNOME桌面环境完整教程
  • 【Linux系统】线程
  • 一种快速计算OTA PSRR的方法(Ⅰ)
  • open files 打开文件数
  • SALOME源码分析: JobManager
  • [更新完毕]2025五一杯B题五一杯数学建模思路代码文章教学: 矿山数据处理问题
  • php artisan resetPass 执行密码重置失败的原因?php artisan resetPass是什么 如何使用?-优雅草卓伊凡
  • PDF转换工具xpdf-tools-4.05
  • 【AI面试准备】AI误判案例知识库优化方案
  • 依赖倒置原则
  • AI外挂RAG:大模型时代的检索增强生成技术
  • 笔试专题(十四)
  • 基于C++、JsonCpp、Muduo库实现的分布式RPC通信框架
  • c语言的常用关键字
  • (六——下)RestAPI 毛子(Http resilience/Refit/游标分页)
  • math.atan2(y, x)