Benyi Hsia

我是Benyi,這裡存放關於資訊科技的文章

PHP 正規表達式匹配中文字及表單驗證

| Comments

正規表達式 (Regular Expression) 是用來處理文字及字串格式的一個方法及表達式。

幾乎所有語言都支援正規表達式的使用,
也有提供相關的函數。

正規表達式可以處理與文字有關的情況,
例如使用者輸入的表單驗證,
出生年月日是否符合格式,
行動電話是不是 09 開頭中間一槓,後面 6 位數…… Email 是不是 xxx@zzz.com 這種格式等等等…。

我們可以用 /[a-zA-Z]+/ 來符合英文字,
例如 ABC abc sexy 會符合規則,
但是 abc123 就不符合
因為我們的規則是 「(英文字母a-z及A-Z) 一次或多次」。

我們也可以利用 ^ 來排除,
例如 /[^a-zA-Z]+/ 代表 「(英文字母a-z及A-Z)以外的任何字 一次或多次」。
所以這時候 123 你好嗎 幸せ 就會附合這個規則,
abcABC 則不會。

但是,假設我們今天要設計一個欄位,可能是姓名,或者是公司名稱,
需要限制欄位的資料不能包含特殊符號,或是只能符合特殊的格式。

這時候如果我們的語言是英文或是其他字母為主的語系,
那還算好處理

因為我們只要設定欄位的規則為 /[a-zA-Z]+/
代表這個欄位只能出現英文字。

這樣,簡簡單單就限制了使用者能輸入的文字,
而不會有其他的例外狀況。

但是,如果是中文呢? 要怎麼限制呢?
把所有的文字抓進來嗎? 把常用的 5000 多個字全部寫進規則裡嗎?

/[一二三四五六七八九十自流認以海美了車文馬總南進信比畫得過整備到著技總景當吸市轉生到故拉色情跟年多香期戲時民品去這因人的趣卻個得們克現就車過的媽原量定提無發八生可馬我大高到經於班表所最時越爸兩比呢自帶才化性義注我種路..]/

那不可能!

還是說用差集的方式,加上剛剛的 ^ 表達式,將特殊符號全部過濾掉?

/[^\!\@\#\$\%\^\&\*\(\)\'\"\[\]\,\.\~\`]/

但是符號這麼多,還有一些根本無法顯示的控制字元,根本無法一次窮舉出來。

爬了一些文,有些是用 unicode 限制範圍

/[\u4e00-\u9fcc]+/

*有的是到 9a05

這個也是一種解法,但是老實說,這樣的寫法也不是很容易閱讀。
其實 PHP 有提供另外一種 Perl 的集合 PCRE (Perl Compatible Regular Expressions) 可以直接套用。

/\p{Han}+/u

中間的 {Han} 表示中文字的意思。(記得行尾的 u,表示 unicode )
這樣子就可以 match 所有中文字,或是排除中文字等等。

也可以處理我們想要的東西,例如「台灣地址」

/^[0-9]{3,5}\s?\p{Han}{1,2}(縣|市)[\p{Han}\w\d\-]+$/u

[0-9]{3,5} 表示郵遞區號數字, 0-9 最少3個,最多5個。
\s? 表示空白可有可無

33325 10023 結束之後,通常就要接縣市,
所以設計了一個 \p{Han}{1,2}(縣|市)
這樣可以符合 桃市 竹縣 高雄市 嘉義縣 等。

PCRE 可用集合

集合 意義
{Hiragana} 平假名
{Katakana} 片假名
... 其他 參考網址

如此一來,比寫 \uXXXX 的代碼來得容易閱讀多
而且撰寫規則時,也很清楚知道什麼字代表什麼意思。

結論

表單驗證的重點主要是在於「允許」(白名單),而不是「框住」(黑名單)。
因為黑名單完全無法窮舉出來。

例如,此欄位「只允許英文」、「只允許數字3~5個」
而不是「除了單引號之外的所有字」

因為永遠不知道使用者輸入什麼給你
況且我們想得到的黑名單只有這些

真正的特殊符號還是有許多無法顯示、無法處理的。

如果你有驗證中文字的需求的話,希望這篇可以給你作為一個參考。

如錯誤還請指教

Comments

comments powered by Disqus