過濾字符串中的特殊符號是表單驗證、文件名淨化、搜索關鍵詞預處理常見需求。本文從原始遍歷替換演進到高性能正則、白名單策略,提供生產級方案。
function strFilter_v1(string $str): string {
$symbols = ['`', '·', '~', '!', '!', '@', '#', '$', '¥', '%', '^', '…', '&', '*',
'(', ')', '(', ')', '-', '_', '—', '+', '=', '|', '\\', '[', ']',
'【', '】', '{', '}', ';', ';', ':', ':', '\'', '"', '“', '”',
',', ',', '<', '>', '《', '》', '.', '。', '/', '、', '?', '?'];
return trim(str_replace($symbols, '', $str));
}
// 缺點:維護困難、易漏字符、性能隨符號表增長線性下降
function strFilter_v2(string $str): string {
// 定義允許保留的字符類(白名單思維)
// 這裡保留:中文、英文、數字、空格、基本標點
$pattern = '/[^\p{Han}\p{L}\p{N}\s\p{P}]/u';
return trim(preg_replace($pattern, '', $str));
}
// \p{Han} 中文字符、\p{L} 所有字母、\p{N} 數字、\p{P} 標點、\s 空白
// u 修飾符啟用 UTF-8 模式
| 場景 | 允許字符集 | 正則模式 |
|---|---|---|
| 用戶名 | 字母、數字、下劃線、中文 | /^[a-zA-Z0-9_\p{Han}]{3,20}$/u |
| 手機號 | 純數字 | /^1[3-9]\d{9}$/ |
| 郵箱 | 標準郵箱格式 | /^[^\s@]+@[^\s@]+\.[^\s@]+$/ |
| 搜索關鍵詞 | 中英數、空格、基本標點 | /[^\p{Han}\p{L}\p{N}\s,。!?、;:“”‘’]/u |
| 文件名 | 中英數、_-.()()[]【】空格 | /[^\p{Han}\p{L}\p{N}\s_\-.()()\[\]【】]/u |
| 純文本內容 | 可見字符(含標點) | /[^\p{L}\p{N}\p{P}\p{Z}]/u |
class StringFilter {
private static array $patterns = [];
public static function filter(string $str, string $scene = 'default'): string {
$pattern = self::getPattern($scene);
return trim(preg_replace($pattern, '', $str));
}
private static function getPattern(string $scene): string {
if (!isset(self::$patterns[$scene])) {
self::$patterns[$scene] = self::buildPattern($scene);
}
return self::$patterns[$scene];
}
private static function buildPattern(string $scene): string {
return match ($scene) {
'username' => '/[^a-zA-Z0-9_\p{Han}]/u',
'search' => '/[^\p{Han}\p{L}\p{N}\s,。!?、;:“”‘’]/u',
'filename' => '/[^\p{Han}\p{L}\p{N}\s_\-.()()\[\]【】]/u',
'default' => '/[^\p{L}\p{N}\p{P}\p{Z}]/u', // 可見字符
};
}
}
// 使用
$clean = StringFilter::filter($userInput, 'search');
\p{L} / \p{Letter}:所有字母\p{N} / \p{Number}:所有數字\p{P} / \p{Punct}:所有標點\p{Z} / \p{Separator}:所有空白(含不可見)\p{Han}:漢字(CJK 統一表意文字)\p{Script=Hiragana}:平假名\p{Emoji}:Emoji (PCRE 10.32+ / PHP 7.3+)strlen/str_replace 按字節操作,破壞 UTF-8,必須用 mb_* 或 preg_* (u 修飾符)@、..、-、+ 導致無法註冊.* 貪婪匹配、嵌套量詞、回溯陷阱,複雜場景用 PCRE_JIT (PHP 7.0+ 默認開啟)| 方案 | 性能 | 維護性 | 準確性 | 適用場景 |
|---|---|---|---|---|
| 遍歷 str_replace | O(n×m) | 差 | 易漏 | 極簡教學 |
| 單一正則黑名單 | O(n) | 中 | 難全 | 通用清理 |
| 場景化白名單正則 | O(n) | 好 | 高 | **生產推薦** |
| filter_var 驗證器 | O(n) | 好 | 標準 | 郵箱、URL、IP、布爾 |
堅持「白名單驗證、場景化正則、Unicode 感知、統一封裝」,告別特殊符號過濾的無限 if-else。