Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

規則運算式 (RegEx) 比對中文字與日文假名

今天在幫同事處理一個公司網站的規則運算式 (regular expression) 問題:我用 ACF 客製化的區塊沒辦法正確產生項目符號。

正常的情況應該是這樣:

但是我同事在寫文章的時候變成這樣:

後來上 Regex 101 實驗之後,才發現是自己的規則運算式有問題。原本用來比對的規則運算式如下:

if ( preg_match( '/(\w+)<br\s*\/*>/', $content ) ) {
  	// 將換行標籤 <br /> 與 <br> 取代為 </li><li>,作為項目符號。
    $content = '<ul><li>' . preg_replace( '/(\w+)<br\s*\/*>/', '$1</li><li>', $content ) . '</li></ul>';
} else {
  	// 如果是沒有斷行,則當作段落元素來處理。
    $content = '<p>' . $content . '</p>';
}

用規則運算式比對多位元組字元 (multibyte character)

原本的規則運算式中,使用了 \w 用來比對字串,但 \w 能比對的,只有半形英數,無法用來比對像是中文漢字或日文假名的多位元組字元 (multibyte characters)。如果要改為能夠比對多位元組字元的話,那需要用 \p{L} 這個特殊的字元類別來表示。

此外,如果回頭參考同事使用的字串內容,會發現他的字串中還有其他比對模式需要處理,例如「全形字與半形字中間應有一個半形空格」、「標點符號 %」等,因此需要將規則運算式改寫為下列形式:

if ( preg_match( '/([\w\p{L}\s%!]+)<br\s*\/*>/', $content ) ) {
  	// 將換行標籤 <br /> 與 <br> 取代為 </li><li>,作為項目符號。
    $content = '<ul><li>' . preg_replace( '/([\w\p{L}\s%!]+)<br\s*\/*>/', '$1</li><li>', $content ) . '</li></ul>';
} else {
  	// 如果是沒有斷行,則當作段落元素來處理。
    $content = '<p>' . $content . '</p>';
}

規則運算式的字元類別

關於規則運算式的一般做法,Harris 先生與 Google Analytics 的說明文章都有基本的介紹。這邊要再補充說明字元類別:

名稱說明
\s泛空白字元 (除了半形空格外,也包含 tab、換行等)
\d任何 10 進位數字,相當於 [0-9]
\S非泛空白字元
\D非 10 進位數字字元
[字元組合]比對存在於 [] 間的字元組合,如 [A-Za-z0-9] 相當於 \w
[^字元組合]比對非存在於 [] 間的字元組合
\w比對任何單位元文字字元
\W比對任何非文字字元

其他字元類別的介紹,可以參考 Microsoft 的開發文件

結語

這次的問題主要是一開始設計不良導致,在測試的過程中,我只用了隨機的英文字串 (如截圖的 abc、def 等),並沒有考慮到實際上寫文章的情形。

Eric Chuang
Eric Chuang

正職是廣告行銷人員,因為 Google Tag Manager 的關係開始踏入網站製作的領域,進一步把 WordPress 當成 PHP + HTML + CSS + JavaScript 的學習教材。此外,因為工作的關係,曾經用 Automattic 的 Underscores (_s) 替客戶與公司官網進行全客製化佈景主題開發。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料