早上看到老趙的《一個較完整的關鍵字過濾解決方案(上)》文章,講到怎樣在項目中嵌入過濾方案的問題,以及提到 xingd 和 sumtec 兩位大師發表的系列互拼的文章,在此我也忍不住談談自己遇到的問題以及一個的簡化版的算法。
因為過濾關鍵字機制到處可見,于是聰明的網友就會想到各種各樣的方法突破,例如:
1、中文會用繁體字的方法避開關鍵字掃描
2、在關鍵字中間插入無意思的特殊字符,例如 * & # @ 等,而且個數可變
3、使用諧音或拆字法變換關鍵字
在實現自己的算法時也有些問題:
4、隨著時間推移,關鍵字列表會越來越大,有些論壇常用的正則表達式N次掃描的方法顯得效率很低。
5、關鍵字有不同的嚴重級別,有些需要禁止,有些只需要替換,還有一些可能記錄一下即可。
針對這些問題,可采用的應對方法:
1、加載關鍵字列表時,將所有的關鍵字轉換成繁體字一份,以掃描繁體版的關鍵字;
這個轉換工作只需一句就可以實現了:
s=Microsoft.VisualBasic.Strings.StrConv(word, Microsoft.VisualBasic.VbStrConv.TraditionalChinese, 0);
2、在掃描原文本時,如果遇到關鍵字的首個文字,忽略其后的特殊字符,直到下一個有意義的文字為止,當然這里需要在定義關鍵字列表時指定哪些才需要這樣掃描,并不是所有關鍵字都采用這種方式;
例如有關鍵字 “你好”經常會被人輸入成“你x好”或者“你xxxxx好”,那么在關鍵字列表里就需要定義成“你*好”,在匹配關鍵字時,如果遇到星號就忽略原文本下一個為特殊的字符。
3、遇到諧音和拆字時,沒什么好辦法了,只好將這些諧音詞和拆分詞也加入到關鍵字列表。
4、不用正則表達式或者 String.IndexOf方法,可以將所有關鍵字的首字相同的組成一個一個小組,然后在將首字放到一個散列表(HashTable/Dictionary),在掃描原文本時先在散列表里掃描,如果碰到了首字再掃描同組的關鍵字,這樣簡單處理一下效率可以提高很多。

還有一個比用散列表更好的方法,將散列表改成一個大小為char.MaxValue的數組,然后將首個文字轉成int,即char->int,然后將關鍵詞集合放到相應下標里。這樣在掃描原文本時,將被掃描的字符轉成int,然后試探數組相應下標的元素是否不為NULL。這樣比用散列表會更快一些。
5、在定義關鍵字時,同時給一個“級別”屬性,例如使用 E,R,B分別表示只記錄、替換、禁止等情況。
于是關鍵字的列表如下所示:
你滾 E
他niang的 R
成*人*網*站 B
這里貼一下關鍵的部分代碼:

Code
private WordGroup[] _wordTable;
public FilterResult Filter(ref string source,char replaceChar)
{
//NOTE::
// 如果方法返回 FilterResult.Replace或者FilterResult.Banned,則原字符串的某些字會被替代為星號,替代后的字符串可以由source取得
if (String.IsNullOrEmpty(source)) return FilterResult.Pass;
FilterResult result = FilterResult.Pass;
char[] tempString = null;
int start = 0;
for (; start < source.Length; start++)
{
WordGroup fw = _wordTable[fastToLower(source[start])];
if (fw != null)
{
for (int idx = 0; idx < fw.Count; idx++)
{
WordEntity we = fw.GetItem(idx);
int matchLength=0;
if (we.Word.Length==0 || checkString(source, we.Word, start + 1, out matchLength))
{
FilterResult fr = we.HandleType;
if (fr > result) result = fr; //記錄最高級別的處理方法
if (fr == FilterResult.Replace || fr == FilterResult.Banned)
{
//替換關鍵字
if(tempString==null) tempString =source.ToCharArray();;
for (int pos = 0; pos < matchLength + 1; pos++)
{
tempString[pos + start] = replaceChar;
}
}
}
}
}
}
if (result > FilterResult.RecordOnly)
{
source = new string(tempString);
}
return result;
}
private bool checkString(string source, string keyword, int sourceStart, out int matchLength)
{
bool found = false;
int sourceOffset = 0;
int keyIndex = 0;
for (; keyIndex < keyword.Length; keyIndex++)
{
if (sourceOffset + sourceStart >= source.Length) break; //原始字符串已經全部搜索完畢
if (keyword[keyIndex] == '*')
{
//跳過可忽略的字符
while (sourceOffset + sourceStart < source.Length)
{
if (isIgnorableCharacter_CN(source[sourceOffset + sourceStart]))
sourceOffset++;
else
break;
}
}
else
{
//比較字母
if (fastToLower(source[sourceOffset + sourceStart]) == (int)keyword[keyIndex])
{
if (keyIndex == keyword.Length - 1)
{
found = true;
break;
}
}
else
{
break;
}
sourceOffset++;//移動原始字符串
}
}
//如果匹配中關鍵字,則返回原字符串中被匹配中的文字的長度,否則返回0
matchLength = sourceOffset + 1;
return found;
}
private int fastToLower(char character)
{
//將大寫英文字母以及全/半角的英文字母轉化為小寫字母
int charVal = (int)character;
if (charVal <= 90)
{
if (charVal >= 65) //字母A-Z
return charVal - 65 + 97;
}
else if (charVal >= 65313)
{
if (charVal <= 65338)
return charVal - 65313 + 97; //全角大寫A-Z
else if (charVal >= 65345 && charVal <= 65370)
return charVal - 65345 + 97; //全角小寫a-z
}
return charVal;
}
private bool isIgnorableCharacter_CN(char character)
{
//NOTE::
// 中文表意字符的范圍 4E00-9FA5
int charVal = (int)character;
return !(charVal >= 0x4e00 && charVal <= 0x9fa5);
}
// 單個過濾詞條目
class WordEntity
{
public string Word { get; set; }
public FilterResult HandleType { get; set; }
}
// 過濾詞的組
class WordGroup
{
//NOTE::用于裝載一組具有同一個字開頭的過濾詞
private List<WordEntity> _words;
public WordGroup()
{
_words = new List<WordEntity>();
}
public void AppendWord(string word, FilterResult handleType)
{
AppendWord(new WordEntity() { Word = word, HandleType = handleType });
}
public void AppendWord(WordEntity word)
{
_words.Add(word);
}
public int Count
{
get { return _words.Count; }
}
public WordEntity GetItem(int index)
{
return _words[index];
}
}
NET技術:一個簡單的關鍵字過濾算法,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。