在開發關鍵字高亮功能時,許多 PHP 開發者都會遭遇同一個棘手問題:本地測試一切正常,一旦部署到線上環境,中文內容卻變成無法閱讀的亂碼。英文處理毫無壓力,但一碰到漢字就頻頻出錯,甚至陷入死循環。這究竟是怎麼回事?
早期的 PHP 內核並非基於 Unicode 設計,而是將字符串視為單字節的 ASCII 序列處理。一個中文字符在 GB2312/GBK 編碼下佔用 2 個位元組,在 UTF-8 下則佔用 3 個位元組。當正則引擎按單字節掃描時,會將一個漢字拆解為多個獨立字節進行匹配,導致模式錯位、匹配失敗甚至產生無效字符組合。
場景一:字符類別匹配中文
preg_match_all("/[漢字]+/ism", "我是漢字,看你把我怎麼著!", $matches);
開發者本意是匹配「漢」或「字」或「漢字」,實際上正則引擎將「漢字」拆解為 4 個位元組(GBK),字符類別 [漢字] 被誤解為匹配這 4 個位元組中的任意一個,結果產生大量亂碼。
場景二:字符串替換誤傷相鄰字符
eregi_replace("性", "沒有", "有責任感");
「有責任感」的 GBK 編碼位元組序列中,「有」的第二字節與「責」的第一字節剛好組成「性」的編碼。PHP 錯誤匹配並替換,導致「用揮敘任感」這樣的離奇結果。連 str_replace() 這類原生函數也無法倖免。
PHP 提供了 mbstring 擴展,專門處理多位元組編碼。所有常用字符串函數都有對應的 mb_* 版本:
mb_ereg_replace() 替代 ereg_replace()mb_strpos() 替代 strpos()mb_substr() 替代 substr()mb_strlen() 替代 strlen()使用前請確認 php.ini 中啟用了 extension=mbstring,並通過 mb_internal_encoding('UTF-8') 設定內部編碼。
若必須使用 PCRE 正則處理中文,請遵循以下原則:
/(漢|字)+/ 優於 /[漢字]+/u 修飾符,如 /漢字+/u中文處理的根本出路在於「編碼一致性」與「多位元組感知函數」。盡早遷移到 PHP 7+ 並全面採用 mbstring 函數、資料庫連線設定 SET NAMES utf8mb4、HTML 頁面宣告 <meta charset="utf-8">,才能從根源上杜絕亂碼困擾。