Unicode Property Escapesについての補説

 ECMAScript 2018よりRegExpで使えるようになったUnicodeプロパティーエスケープ (\p{...}, \P{...}) についての補説です。

メニュー

書式

 ECMAScript (JavaScript) の正規表現では、\p{...}, \P{...} ともに次のどちらかの書式を取ります。

 いずれの書式も{}内では大文字小文字が区別されます。

\p{NAME=VALUE}型

 \p{NAME=VALUE} という表現は、「Unicodeの全コードポイントのうち、NAMEという属性の値がVALUEであるものすべて」にマッチします。例えば \p{sc=Hira} という表記は、「Unicodeに存在するあらゆるひらがな」にマッチします。
 \P{...} は \p{...} とは逆に、「NAMEという属性の値がVALUEではないものすべて」にマッチします。\P{sc=Hira} という表記は「Unicodeに存在するひらがな以外のあらゆる文字」にマッチします。

 Unicode規格には様々な種類の属性が存在しますが、ECMAScriptではNAMEの部分に指定できる属性名を次の3つに限っています。

 前述の通り \p{...}, \P{...} とも ... の部分では大文字小文字が区別されますので、\p{GC=何々} や \P{script=何々} のように書くと認識されずエラーとなります。

General_Categoryの値一覧

 General_Categoryの値として指定できるのは次の文字列です。

General_Categoryの値一覧(括弧内は別名)
Cased_Letter (LC) Close_Punctuation (Pe) Connector_Punctuation (Pc) Control (Cc, cntrl)
Currency_Symbol (Sc) Dash_Punctuation (Pd) Decimal_Number (Nd, digit) Enclosing_Mark (Me)
Final_Punctuation (Pf) Format (Cf) Initial_Punctuation (Pi) Letter (L)
Letter_Number (Nl) Line_Separator (Zl) Lowercase_Letter (Ll) Mark (M, Combining_Mark)
Math_Symbol (Sm) Modifier_Letter (Lm) Modifier_Symbol (Sk) Nonspacing_Mark (Mn)
Number (N) Open_Punctuation (Ps) Other (C) Other_Letter (Lo)
Other_Number (No) Other_Punctuation (Po) Other_Symbol (So) Paragraph_Separator (Zp)
Private_Use (Co) Punctuation (P, punct) Separator (Z) Space_Separator (Zs)
Spacing_Mark (Mc) Surrogate (Cs) Symbol (S) Titlecase_Letter (Lt)
Unassigned (Cn) Uppercase_Letter (Lu)

Script, Script_Extensionsの値一覧

 ScriptおよびScript_Extensionsの値として指定できるのは次の文字列です(ECMAScript 2019/ES10現在)。

Script, Script_Extensionsの値一覧(括弧内は別名)
Adlam (Adlm) Ahom (Ahom) Anatolian_Hieroglyphs (Hluw) Arabic (Arab)
Armenian (Armn) Avestan (Avst) Balinese (Bali) Bamum (Bamu)
Bassa_Vah (Bass) Batak (Batk) Bengali (Beng) Bhaiksuki (Bhks)
Bopomofo (Bopo) Brahmi (Brah) Braille (Brai) Buginese (Bugi)
Buhid (Buhd) Canadian_Aboriginal (Cans) Carian (Cari) Caucasian_Albanian (Aghb)
Chakma (Cakm) Cham (Cham) Cherokee (Cher) Common (Zyyy)
Coptic (Copt, Qaac) Cuneiform (Xsux) Cypriot (Cprt) Cyrillic (Cyrl)
Deseret (Dsrt) Devanagari (Deva) Dogra (Dogr) Duployan (Dupl)
Egyptian_Hieroglyphs (Egyp) Elbasan (Elba) Ethiopic (Ethi) Georgian (Geor)
Glagolitic (Glag) Gothic (Goth) Grantha (Gran) Greek (Grek)
Gujarati (Gujr) Gunjala_Gondi (Gong) Gurmukhi (Guru) Han (Hani)
Hangul (Hang) Hanifi_Rohingya (Rohg) Hanunoo (Hano) Hatran (Hatr)
Hebrew (Hebr) Hiragana (Hira) Imperial_Aramaic (Armi) Inherited (Zinh, Qaai)
Inscriptional_Pahlavi (Phli) Inscriptional_Parthian (Prti) Javanese (Java) Kaithi (Kthi)
Kannada (Knda) Katakana (Kana) Kayah_Li (Kali) Kharoshthi (Khar)
Khmer (Khmr) Khojki (Khoj) Khudawadi (Sind) Lao (Laoo)
Latin (Latn) Lepcha (Lepc) Limbu (Limb) Linear_A (Lina)
Linear_B (Linb) Lisu (Lisu) Lycian (Lyci) Lydian (Lydi)
Mahajani (Mahj) Makasar (Maka) Malayalam (Mlym) Mandaic (Mand)
Manichaean (Mani) Marchen (Marc) Masaram_Gondi (Gonm) Medefaidrin (Medf)
Meetei_Mayek (Mtei) Mende_Kikakui (Mend) Meroitic_Cursive (Merc) Meroitic_Hieroglyphs (Mero)
Miao (Plrd) Modi (Modi) Mongolian (Mong) Mro (Mroo)
Multani (Mult) Myanmar (Mymr) Nabataean (Nbat) New_Tai_Lue (Talu)
Newa (Newa) Nko (Nkoo) Nushu (Nshu) Ogham (Ogam)
Ol_Chiki (Olck) Old_Hungarian (Hung) Old_Italic (Ital) Old_North_Arabian (Narb)
Old_Permic (Perm) Old_Persian (Xpeo) Old_Sogdian (Sogo) Old_South_Arabian (Sarb)
Old_Turkic (Orkh) Oriya (Orya) Osage (Osge) Osmanya (Osma)
Pahawh_Hmong (Hmng) Palmyrene (Palm) Pau_Cin_Hau (Pauc) Phags_Pa (Phag)
Phoenician (Phnx) Psalter_Pahlavi (Phlp) Rejang (Rjng) Runic (Runr)
Samaritan (Samr) Saurashtra (Saur) Sharada (Shrd) Shavian (Shaw)
Siddham (Sidd) SignWriting (Sgnw) Sinhala (Sinh) Sogdian (Sogd)
Sora_Sompeng (Sora) Soyombo (Soyo) Sundanese (Sund) Syloti_Nagri (Sylo)
Syriac (Syrc) Tagalog (Tglg) Tagbanwa (Tagb) Tai_Le (Tale)
Tai_Tham (Lana) Tai_Viet (Tavt) Takri (Takr) Tamil (Taml)
Tangut (Tang) Telugu (Telu) Thaana (Thaa) Thai (Thai)
Tibetan (Tibt) Tifinagh (Tfng) Tirhuta (Tirh) Ugaritic (Ugar)
Vai (Vaii) Warang_Citi (Wara) Yi (Yiii) Zanabazar_Square (Zanb)

 HiraganaとKatakanaはあるのにKanjiがないと思われるかもしれませんが、Han (Hani) というのが漢字のことです(「漢字」の中国語読みをローマ字化したHanziに由来)。

ScriptとScript_Extensionsとの違い

 ScriptというのはUnicodeにおけるもっとも基本的な文字の分類単位です。日本語では文字体系、書記体系などと訳されることもありますが、今のところ定訳と呼べるようなものはまだないようです。
 例えばひらがなの「あ」(U+3042) は、Unicodeにおいては "Hiragana" scriptに属する文字のうちの一つであると見なされます。

 Unicodeでは文字を符号化する際、その符号化しようとしている文字がどのscriptに分類されるかをまず考えます。もし既存のどのscriptにも属するものではないと判断されれば、新しいscript名が登録され、その上で文字の符号化が行われます。いわばscriptとはディレクトリのようなもので、例えば先のひらがな「あ」であれば Unicode/Hiragana/あ のようにイメージしていただくと分かりやすいかもしれません。

 同名のファイルでもpathが異なれば別物であるように、見た目や発音が同じでもscriptが異なればUnicodeでは別の文字として扱われます。ラテン文字のA (U+0041)、ギリシア文字のΑ (U+0391)、キリル文字のА (U+0410) がUnicodeで区別されるのも、それぞれ別のscriptに属しているためです。ひらがなの「へ(U+3078)」とカタカナの「ヘ(U+30D8)」に別々の文字コードが割り振られているのも同じ理由です。

「異なるscriptに属する文字同士は、たとえ互いに似ていても別々の文字として扱われる」ということは、言い方を変えれば「1つの文字は複数のscriptに属することはできない」ということでもあります。従いましてUnicodeに存在するすべての文字は、1種類の \p{Script=何々} にしかマッチしません。

 しかし現実には、複数のscriptに跨って現れる文字というものも存在します。身近なところですと長音符・長音記号の「ー」がそうです。この文字は「KATAKANA-HIRAGANA PROLONGED SOUND MARK」というUnicode名が示す通り、Hiragana script とも Katakana script ともどちらとも組み合わせて使われます。このような場合、どちらかのscriptに属させるわけにも行きませんので Common(汎用)という名前のscriptに分類されています。従って \p{sc=Hiragana} も \p{sc=Katakana} も「ー」にはマッチしません。

 このような文字が \p{何々=Hiragana} とも \p{何々=Katakana} ともどちらともマッチするようにしたのがScript_Extensionsです。例えば \p{scx=Hiragana} とすれば、先述の音引き棒のみならず「、。・゛゜」などにもマッチするようになります。さらにこれら「、。・゛゜ー」は、\p{scx=Katakana} ともマッチします。

 Script_Extensionsにすると具体的にどの文字がどう変わるかについては、Scriptデータに対する差分の形で公開されています。

\p{LoneNameOrValue}型

 この書式が指定された場合、正規表現コンパイラはまず \p{General_Category=...} の "General_Category=" の部分が省略されたものと仮定し、General_Categoryとして指定できる値一覧の中に LoneNameOrValue と一致するものがあるかどうか調べます。
 その結果もしあれば、\p{General_Category=LoneNameOrValue} として処理を続けます。

 もしなかった場合、LoneNameOrValueの部分は binary property であると解釈され、この表現は「Unicodeの全コードポイントのうち、LoneNameOrValueで指定された属性値を持つものすべて」にマッチします。例えば \p{White_Space} という表記は、Unicodeに存在するあらゆる空白文字にマッチします。

Binary Property名一覧

 LoneNameOrValueの部分に指定できるのは次の文字列です(ECMAScript 2019/ES10現在)。

Binary Property名一覧(括弧内は別名)
ASCII ASCII_Hex_Digit (AHex)
Alphabetic (Alpha) Any
Assigned Bidi_Control (Bidi_C)
Bidi_Mirrored (Bidi_M) Case_Ignorable (CI)
Cased Changes_When_Casefolded (CWCF)
Changes_When_Casemapped (CWCM) Changes_When_Lowercased (CWL)
Changes_When_NFKC_Casefolded (CWKCF) Changes_When_Titlecased (CWT)
Changes_When_Uppercased (CWU) Dash
Default_Ignorable_Code_Point (DI) Deprecated (Dep)
Diacritic (Dia) Emoji
Emoji_Component Emoji_Modifier
Emoji_Modifier_Base Emoji_Presentation
Extender (Ext) Extended_Pictographic
Grapheme_Base (Gr_Base) Grapheme_Extend (Gr_Ext)
Hex_Digit (Hex) IDS_Binary_Operator (IDSB)
IDS_Trinary_Operator (IDST) ID_Continue (IDC)
ID_Start (IDS) Ideographic (Ideo)
Join_Control (Join_C) Logical_Order_Exception (LOE)
Lowercase (Lower) Math
Noncharacter_Code_Point (NChar) Pattern_Syntax (Pat_Syn)
Pattern_White_Space (Pat_WS) Quotation_Mark (QMark)
Radical Regional_Indicator (RI)
Sentence_Terminal (STerm) Soft_Dotted (SD)
Terminal_Punctuation (Term) Unified_Ideograph (UIdeo)
Uppercase (Upper) Variation_Selector (VS)
White_Space (space) XID_Continue (XIDC)
XID_Start (XIDS)
  • この色の1つはECMAScript 2019で追加されたものです。

 ちなみに省略が認められているのは "General_Category=" のみで、"Script=" や "Script_Extensions=" の省略は認められていません。これらは=の右辺値が同じですので、\p{Katakana} のような表現だけでは \p{sc=Katakana} なのか \p{scx=Katakana} なのか区別が付かないためです。

参考資料