Unicode 字元集為世界大部份文字系統做了整理,規則表示式是為了比對文字,兩者相遇就產生了更多的需求;為了能令規則表示式支援 Unicode,Unicode 組織在〈UNICODE REGULAR EXPRESSIONS〉做了規範;JavaScript 對 Unicode 規則表示式,從 ES6 開始逐步支援,想運用的話,必須開啟 u
旗標採用 Unicode 模式。
Unicode 模式
從 ES6 開始,規則表示式啟用 u
旗標,代表著啟用 Unicode 模式,目的之一是支援 \u{…}
的寫法;例如,ES6 以後 '\uD834\uDD1E'
可以使用 '\u{1D11E}'
來表示;然而,若要在規則表示式使用 \u{…}
,必須開啟 u
旗標:
> 'Treble clef: \uD834\uDD1E'.search(/\u{1D11E}/)
-1
> 'Treble clef: \uD834\uDD1E'.search(/\u{1D11E}/u)
13
>
在啟用 Unicode 模式的情況下,既有的 \uhhhh
寫法就是指定「碼點」(而不是碼元),也就是說,若 \uhhhh
指定的碼點處,實際上沒有定義 Unicode 字元,就不會比對成功。例如:
> let trebleClef = '\u{1D11E}'
undefined
> /\uD834/.test(trebleClef)
true
> /\uD834/u.test(trebleClef)
false
>
在沒有開啟 u
旗標的情況下,test
方法會在字串索引 0 處找到相符的碼元;然而,在開啟 Unicode 模式後,\uD834
就是指 Unicode 碼點 U+D834,然而該碼點處未定義字元,test
就因搜尋失敗而傳回 false
。
啟用 Unicode 模式後,對於 0xFFFF 以外的字元,才會進行正確的辨識,這會影響預定義字元類、量詞等的判斷。
例如,未啟用 Unicode 模式前,預定義字元類 \S
表示非空白字元,然而,對 0xFFFF 以外的字元會誤判,只有在加上 u
旗標的情況下才會正確比對;類似地,\W
、「.
」也只有在啟用 Unicode 模式下,才能正確比對 0xFFFF 以外的字元:
> /^\S$/.test('\u{1D11E}')
false
> /^\S$/u.test('\u{1D11E}')
true
> /^\W$/.test('\u{1D11E}')
false
> /^\W$/u.test('\u{1D11E}')
true
> /^.$/.test('\u{1D11E}')
false
> /^.$/u.test('\u{1D11E}')
true
>
Unicode 特性轉譯
ES9 以後支援規則表示式的 Unicode 特性轉譯(Unicode property escapes),為了能運用這個新功能,必須認識 Unicode 規範中的分類(Category)、文字(Script)。
在 Unicode 的規範中,每個 Unicode 字元會隸屬於某個分類,在〈General Category Property〉中可以看到 Letter、Uppercase Letter 等一般分類,每個分類也給予了 L、Lu 等縮寫名稱。
舉例來說,隸屬於 Letter 分類的字元都是字母,a 到 z、A 到 Z、全形的 a 到 z、A 到 Z 都在 Letter 分類中,除了英文字母之外,其他如希臘字母 α、β、γ 等,也都隸屬於 Letter 分類。
ES9 以後若使用 u
旗標開啟 Unicode 模式,可以使用 \p{General_Category=Letter}
、\p{gc=Letter}
、\p{Letter}
、\p{L}
等方式來指定分類,若分類名稱有兩個字,要使用 _
相連,這樣在使用規則表示式判斷字母、數字等,就非常方便了。例如:
> /\p{General_Category=Letter}/u.test('α')
true
> /\p{gc=Letter}/u.test('α')
true
> /\p{Uppercase_Letter}/u.test('α')
false
> /\p{Ll}/u.test('α')
true
> /\p{Number}/u.test('1')
true
> /\p{Number}/u.test('1')
true
>
\p{…}
的相反就是 \P{…}
:
> /\p{Number}/u.test('1')
true
> /\P{Number}/u.test('1')
false
>
來個有趣的測試吧!𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼 都是十進位數字:
> /^\p{Decimal_Number}+$/u.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼')
true
>
數字呢?²³¹¼½¾𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼㉛㉜㉝ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ都是:
> /^\p{Number}+$/u.test('²³¹¼½¾𝟏𝟐𝟑𝟜𝟏𝟐𝟑𝟜���𝟪𝟫𝟬𝟭𝟮㉛㉛㉜㉜㉝㉝ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ')
true
>
Unicode 將希臘文、漢字等組織為文字(Script)特性,可參考〈UNICODE SCRIPT PROPERTY〉,例如,如果想在規則表示式中以文字特性比對,可以使用 \p{Script=Greek}
、\p{Script_Extensions=Greek}
、\p{sc=Han}
、\p{scx=Han}
的寫法(Han 包含了正體中文、簡體中文,以及日、韓、越南文的全部漢字)。例如:
> /\p{Script=Greek}/u.test('a')
false
> /\p{Script_Extensions=Greek}/u.test('α')
true
> /\p{sc=Greek}/u.test('α')
true
> /\p{sc=Han}/u.test('林')
true
>
另外還有一些二元特性,像是 ASCII、Alphabetic、Lowercase、Emoji 等,直接寫在 \p{..}
裏就可以了。例如:
> /\p{Emoji}/u.test('😃')
true
>
如果想取得 \p{..}
中可以使用的特性名稱等清單,可以查閱 ECMAScript 規格書〈UnicodeMatchProperty〉與〈UnicodeMatchPropertyValue〉內容。