內容審核系統需過濾敏感詞彙,但單純替換會誤傷正常詞彙(如「曹操」被過濾成「曹**」)。本文實作 StrFilter 類別,採用白名單保護機制:先將白名單詞彙暫時替換為特殊標記,執行黑名單替換後再還原白名單,精準避免誤傷。
核心邏輯
- 保護白名單:將白名單詞彙替換為唯一標記(如 `[[##key##]]`)。
- 替換黑名單:對剩餘內容執行敏感詞替換。
- 還原白名單:將標記替換回原始白名單詞彙。
類別程式碼
<?php
class StrFilter {
private $_white_list = [];
private $_black_list = [];
private $_replacement = '*';
private $_LTAG = '[[##';
private $_RTAG = '##]]';
public function __construct(array $white_list = [], array $black_list = [], $replacement = '*') {
$this->_white_list = $white_list;
$this->_black_list = $black_list;
$this->_replacement = $replacement;
}
public function replace($content) {
if (!isset($content) || $content === '') return '';
// 1. 保護白名單
$content = $this->protect_white_list($content);
// 2. 替換黑名單
if ($this->_black_list) {
foreach ($this->_black_list as $word) {
$content = str_replace($word, $this->_replacement, $content);
}
}
// 3. 還原白名單
$content = $this->resume_white_list($content);
return $content;
}
public function check($content) {
if (!isset($content) || $content === '') return true;
$content = $this->protect_white_list($content);
if ($this->_black_list) {
foreach ($this->_black_list as $word) {
if (strstr($content, $word) !== false) return false;
}
}
return true;
}
private function protect_white_list($content) {
if ($this->_white_list) {
foreach ($this->_white_list as $key => $word) {
$content = str_replace($word, $this->_LTAG . $key . $this->_RTAG, $content);
}
}
return $content;
}
private function resume_white_list($content) {
if ($this->_white_list) {
$content = preg_replace_callback(
'/' . preg_quote($this->_LTAG, '/') . '(.*?)' . preg_quote($this->_RTAG, '/') . '/s',
[$this, 'getval'],
$content
);
}
return $content;
}
private function getval($matches) {
return isset($this->_white_list[$matches[1]]) ? $this->_white_list[$matches[1]] : '';
}
}
?>
使用範例
<?php
header('Content-Type: text/html; charset=utf-8');
require 'StrFilter.class.php';
$white = ['屌絲', '曹操']; // 白名單:受保護詞彙
$black = ['屌', '操']; // 黑名單:敏感字元
$content = '我操,曹操你是屌絲,我屌你啊';
$filter = new StrFilter($white, $black);
echo $filter->replace($content);
執行過程追蹤
| 階段 | 內容 |
| 原始 | 我操,曹操你是屌絲,我屌你啊 |
| 保護白名單後 | 我操,[[##1##]]你是[[##0##]],我屌你啊 |
| 替換黑名單後 | 我*,[[##1##]]你是[[##0##]],我*你啊 |
| 還原白名單後 | 我*,曹操你是屌絲,我*你啊 |
輸出結果
我*,曹操你是屌絲,我*你啊
進階擴充建議
- 支援正則黑名單:`$black_list` 可存儲正則模式,`replace` 時使用 `preg_replace`。
- 分級替換字元:根據敏感等級使用不同替換字元(`*`、`#`、`[屏蔽]`)。
- 大小寫不敏感:`str_ireplace` 或正則 `/i` 模式。
- 詞彙庫外部化:從資料庫/快取載入黑白名單,支援熱更新。
- Trie 樹優化:大量敏感詞(>1萬)時,採用 Trie 樹(AC 自動機)實現 O(n) 多模式匹配,避免多次遍歷字串。
- 上下文感知:結合分詞技術(如 jieba-php),語義級過濾而非單純字串匹配。
效能對比