將關鍵詞構造成一顆樹,每個字都是一個節點。
遍歷需要過濾的語句,將語句的每個字都去樹中查詢,看看是否存在。
構造一棵樹簡單,關鍵點是php
中遍歷字串需要自己正確的得到單個字元的長度。
簡單遍歷字串的方法如下:
$strLen = mb_strlen($str);
for ($i = 0; $i < $strLen; $i++) {
echo mb_substr($str, $i, 1, "utf8"),PHP_EOL;
}
登入後複製
該方法是利用mb_*
系列函數來正確擷取每個字元,處理大量字串時速度非常慢,我猜測是:mb_substr
每擷取一個字元,都要計算該字串之前,有多少個字元。
正確的遍歷字串的方式是按utf8
的編碼規律來擷取字串,具體請看下文。
<?php
/**
* 非法關鍵詞檢查
*/
class SensitiveWords
{
protected $tree = null;
protected $callIsNumeric = true;
/**
* 非法詞彙列表,一個非法詞彙佔用一行
*/
public function __construct($path = __DIR__ . '/sensitiveWords.txt')
{
$this->tree = new WordNode();
$file = fopen($path, "r");
while (!feof($file)) {
$words = trim(fgets($file));
if ($words == '') {
continue;
}
//存在純數位的非法詞彙
if (is_numeric($words)) {
$this->callIsNumeric = false;
}
$this->setTree($words);
}
fclose($file);
}
protected function setTree($words)
{
$array = $this->strToArr($words);
$tree = $this->tree;
$l = count($array) - 1;
foreach ($array as $k => $item) {
$tree = $tree->getChildAlways($item);
if ($l == $k) {
$tree->end = true;
}
}
}
/**
* 返回包含的非法詞彙
* @param string $str
* @return array
*/
public function check($str)
{
//先壓縮字串
$str = trim(str_replace([' ', "\n", "\r"], ['', '', ''], $str));
$ret = [];
loop:
$strLen = strlen($str);
if ($strLen === 0) {
return array_unique($ret);
}
//非法詞彙中沒有純數位的非法詞彙,待檢測字串又是純數位的,則跳過不再檢查
if ($this->callIsNumeric && is_numeric($str)) {
return array_unique($ret);
}
//挨個字元進行判斷
$tree = $this->tree;
$words = '';
for ($i = 0; $i < $strLen; $i++) {
//unicode範圍 --> ord 範圍
//一位元組 0-127 --> 0 - 127
//二位元組 128-2047 --> 194 - 223
//三位元組 2048-65535 --> 224 - 239
//四位元組 65536-1114111 --> 240 - 244
//@see http://shouce.jb51.net/gopl-zh/ch3/ch3-05.html
$ord = ord($str[$i]);
if ($ord <= 127) {
$word = $str[$i];
} elseif ($ord <= 223) {
$word = $str[$i] . $str[$i + 1];
$i += 1;
} elseif ($ord <= 239) {
$word = $str[$i] . $str[$i + 1] . $str[$i + 2];
$i += 2;
} elseif ($ord <= 244) {
//四位元組
$word = $str[$i] . $str[$i + 1] . $str[$i + 2] . $str[$i + 3];
$i += 3;
} else {
//五位元組php都溢位了
//Parse error: Invalid UTF-8 codepoint escape sequence: Codepoint too large
continue;
}
//判斷當前字元
$tree = $tree->getChild($word);
if (is_null($tree)) {
//當前字不存在,則擷取後再次迴圈
$str = substr($str, $i + 1);
goto loop;
} else {
$words .= $word;
if ($tree->end) {
$ret[] = $words;
}
}
}
return array_unique($ret);
}
protected function strToArr($str)
{
$array = [];
$strLen = mb_strlen($str);
for ($i = 0; $i < $strLen; $i++) {
$array[] = mb_substr($str, $i, 1, "utf8");
}
return $array;
}
}
/**
* 單個字元的節點
*/
class WordNode
{
//是否為非法詞彙末級節點
public $end = false;
//子節點
protected $child = [];
/**
* @param string $word
* @return WordNode
*/
public function getChildAlways($word)
{
if (!isset($this->child[$word])) {
$this->child[$word] = new self();
}
return $this->child[$word];
}
/**
* @param string $word
* @return WordNode|null
*/
public function getChild($word)
{
if ($word === '') {
return null;
}
if (isset($this->child[$word])) {
return $this->child[$word];
}
return null;
}
}
登入後複製
推薦學習:《》
php入門到就業線上直播課:
以上就是PHP實現非法詞彙過濾(演演算法分析)的詳細內容,更多請關注TW511.COM其它相關文章!