PHP波斯语敏感词过滤正则求助:重复字符与单字空格处理
嘿,这两个问题其实都是多字节字符编码和正则匹配逻辑的坑,我帮你逐个修正:
问题1:波斯语重复字符无法去除
你原来的正则preg_replace('/(.)\1+/', '$1', $text)对英语有效,是因为英语是单字节字符,但波斯语属于UTF-8多字节字符,PCRE默认会把字符串拆成字节处理,导致(.)只能匹配单个字节,而不是完整的波斯语字符(比如ی是多个字节)。
修正方案:
给正则加上/u修饰符,让PCRE以UTF-8编码解析字符串,这样就能正确匹配单个波斯语字符:
$text = preg_replace('/(.)\1+/u', '$1', $text);
测试一下:输入امیییییییییین,处理后会变成امین,完全符合预期。
为什么
mb_ereg_replace无效?因为mb_ereg系列函数的正则语法和PCRE略有不同,而且需要确保你的PHP环境正确设置了默认编码(比如mb_internal_encoding('UTF-8')),但用preg_replace加/u是更简单可靠的方案。
问题2:合并带空格的单字(如S E X → sex)
你原来的正则preg_replace('/( [a-zA-Zآ-ی] )\1+/', trim('$1'), $text)逻辑完全错了:\1+表示重复匹配第一个捕获组的内容,也就是只能匹配“ S S S”这种重复的带空格字符,而无法匹配“S E X”这种不同字符用空格分隔的场景。
修正方案:
我们需要匹配任意连续的单个目标字符(英语/波斯语),字符之间有一个或多个空格,然后去掉空格并统一转小写(如果需要)。这里用preg_replace_callback更灵活:
$text = preg_replace_callback('/(?:\s*[a-zA-Zآ-ی]\s*)+/u', function($match) { // 先去掉所有空格 $cleanStr = preg_replace('/\s+/', '', $match[0]); // 转小写(英语需要,波斯语可根据需求去掉这行) return strtolower($cleanStr); }, $text);
测试输入S E X,处理后会变成sex;如果是波斯语的ا م ی ن,会变成امین。
如果想更简洁,也可以用全局替换直接去掉字符间的空格(适合单次替换场景):
// 匹配“字符+空格+字符”的模式,替换成“字符字符”,全局处理 $text = preg_replace('/([a-zA-Zآ-ی])\s+([a-zA-Zآ-ی])/u', '$1$2', $text); // 重复替换直到没有空格(因为一次替换只能处理相邻的两个字符) while(preg_match('/([a-zA-Zآ-ی])\s+([a-zA-Zآ-ی])/u', $text)) { $text = preg_replace('/([a-zA-Zآ-ی])\s+([a-zA-Zآ-ی])/u', '$1$2', $text); }
不过回调函数的方式更高效,只需要遍历一次字符串。
内容的提问来源于stack exchange,提问作者Mohammad Amin Ghaseminia

