前体
有(正如评论中多次指出的那样)你 - 和/或你的代码 - 通过实现这样的功能陷入了巨大的漏洞,仅举几例:
- 人们会添加字符来欺骗过滤器
- 人们将变得有创造力(例如影射)
- 人们会使用被动攻击和讽刺
- 人们会使用句子/短语而不仅仅是单词
你最好实施一个审核/标记系统,人们可以标记攻击性评论,然后可以由模组、用户等编辑/删除。
基于这个理解,让我们继续……
解决方案
鉴于您:
- 有一个禁用词表
$bad_words
- 有一个替换词表
$good_words
- 不分大小写要换坏词
- 想用随机的好词替换坏词
- 有一个正确转义的坏词列表:见http://php.net/preg_quote
你可以很容易地使用PHP
spreg_replace_callback
函数:
$input_string = 'This Could be interesting but should it be? Perhaps this \'would\' work; or couldn\'t it?';
$bad_words = array('could', 'would', 'should');
$good_words = array('might', 'will');
function replace_words($matches){
global $good_words;
return $matches[1].$good_words[rand(0, count($good_words)-1)].$matches[3];
}
echo preg_replace_callback('/(^|\b|\s)('.implode('|', $bad_words).')(\b|\s|$)/i', 'replace_words', $input_string);
好的,preg_replace_callback
它的作用是编译一个包含所有坏词的正则表达式模式。比赛将采用以下格式:
/(START OR WORD_BOUNDARY OR WHITE_SPACE)(BAD_WORD)(WORD_BOUNDARY OR WHITE_SPACE OR END)/i
i
修饰符使其不区分大小写,因此两者都bad
匹配Bad
。
然后该函数replace_words
获取匹配的单词及其边界(空白或空白字符)并将其替换为边界和随机的好单词。
global $good_words; <-- Makes the $good_words variable accessible from within the function
$matches[1] <-- The word boundary before the matched word
$matches[3] <-- The word boundary after the matched word
$good_words[rand(0, count($good_words)-1] <-- Selects a random good word from $good_words
匿名函数
您可以在preg_replace_callback
echo preg_replace_callback(
'/(^|\b|\s)('.implode('|', $bad_words).')(\b|\s|$)/i',
function ($matches) use ($good_words){
return $matches[1].$good_words[rand(0, count($good_words)-1)].$matches[3];
},
$input_string
);
函数包装
如果您要多次使用它,您也可以将其编写为一个独立的函数,尽管在这种情况下,您很可能希望在调用它时将好/坏词输入函数(或将它们永久地硬编码在那里)但这取决于你如何获得它们......
function clean_string($input_string, $bad_words, $good_words){
return preg_replace_callback(
'/(^|\b|\s)('.implode('|', $bad_words).')(\b|\s|$)/i',
function ($matches) use ($good_words){
return $matches[1].$good_words[rand(0, count($good_words)-1)].$matches[3];
},
$input_string
);
}
echo clean_string($input_string, $bad_words, $good_words);
输出
使用第一个示例中显示的输入和单词列表连续运行上述函数:
This will be interesting but might it be? Perhaps this 'will' work; or couldn't it?
This might be interesting but might it be? Perhaps this 'might' work; or couldn't it?
This might be interesting but will it be? Perhaps this 'will' work; or couldn't it?
当然,替换词是随机选择的,所以如果我刷新页面,我会得到别的东西......但这显示了什么会/不会被替换。
注意
逃跑$bad_words
foreach($bad_words as $key=>$word){
$bad_words[$key] = preg_quote($word);
}
单词边界\b
在这段代码中,我使用\b
,\s
和^
or$
作为单词边界,这是有充分理由的。虽然white space
、start of string
和end of string
都被视为单词边界\b
,但并非在所有情况下都匹配,例如:
\b\$h1t\b <---Will not match
这是因为\b
匹配非单词字符(即[^a-zA-Z0-9]
)和类似字符的字符$
不算作单词字符。
杂项
根据您的单词列表的大小,有几个潜在的问题。从系统设计的角度来看,拥有大量正则表达式通常是一种糟糕的形式,原因如下:
- 可能很难维护
- 很难阅读/理解它的作用
- 很难发现错误
- 如果列表太大,可能会占用大量内存
鉴于正则表达式模式是由PHP
第一个原因被否定的。第二个也应该被否定;如果你的单词列表很大,每个坏词都有十几个排列,那么我建议你停下来重新考虑你的方法(阅读:使用标记/审核系统)。
澄清一下,我不认为有一个问题是有一个小的单词列表来过滤特定的脏话,因为它有一个目的:阻止用户彼此爆发;当您尝试过滤掉太多(包括排列)时,问题就来了。坚持过滤常见的脏话,如果这不起作用,那么 -最后一次- 实施标记/审核系统。