因为过滤关键字机制到处可见,于是聪明的网友就会想到各种各样的方法突破,例如:
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<T>),在扫描原文本时先在散列表里扫描,如果碰到了首字再扫描同组的关键字,这样简单处理一下效率可以提高很多。 还有一个比用散列表更好的方法,将散列表改成一个大小为
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) {
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++;
} }
matchLength = sourceOffset +
1 ;
return found; }
private int fastToLower(
char character) {
int charVal = (
int )character;
if (charVal <=
90 ) {
if (charVal >=
65 )
return charVal -
65 +
97 ; }
else if (charVal >=
65313 ) {
if (charVal <=
65338 )
return charVal -
65313 +
97 ;
else if (charVal >=
65345 && charVal <=
65370 )
return charVal -
65345 +
97 ;
}
return charVal; }
private bool isIgnorableCharacter_CN(
char character) {
int charVal = (
int )character;
return !(charVal >=
0x4e00 && charVal <=
0x9fa5 ); }
class WordEntity {
public string Word { get; set; }
public FilterResult HandleType { get; set; } }
class WordGroup {
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]; } }