正規表現 \p{...} メモ


複数のプログラミング言語で正規表現を使いまわそうとしたら \p{...} (Unicodeプロパティエスケープ)の互換性がよくわからなかったのでメモ。

概要

下表のように、ECMAScript, .NET (C#), Javaでは \p{カテゴリ名} 形式のGeneral Cateogry指定以外の記法は三者に互換性がない。

Unicodeプロパティ ECMAScript .NET Java
General_Category \p{Lu}
\p{General_Category=Lu}
\p{gc=Lu}
\p{Lu} \p{Lu}
\p{IsLu}
\p{General_Category=Lu}
\p{gc=Lu}
Block - \p{IsMongolian} \p{InMongolian}
\p{Block=Mongolian}
\p{blk=Mongolian}
Script \p{Script=Common}
\p{sc=Common}
- \p{IsCommon}
\p{Script=Common}
\p{sc=Common}
Script_Extensions \p{Script_Extensions=Adlam}
\p{scx=Adlam}
- -
Binary Property \p{Uppercase} - \p{IsUppercase}

以下に、General_Category, Block, Script, Script_Extensionsの取る値を、ひらがなやカタカナを例に説明する。

文字 コードポイント 名前 General_Category Block Script Script_ Extensions
U+3048 HIRAGANA LETTER E Lo Hiragana Hiragana {Hiragana}
U+30A8 KATAKANA LETTER E Lo Katakana Katakana {Katakana}
U+30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK Lm Katakana Common {Hiragana, Kanakana}
𛀁 U+1B001 HIRAGANA LETTER ARCHAIC YE Lo Kana Supplement Hiragana {Hiragana}

General_Categoryはコードポイントの最も一般的な分類を表す。ひらがなやカタカナは、大文字小文字といった区別のない文字なのでLo(Letter Other)カテゴリに属し、音引きは他の文字のあとについて長音を表す文字なのでLm(Letter Modifier)カテゴリに属す。

Blockは連続する符号位置の範囲に名前をつけたものである。や行え(HIRAGANA LETTER ARCHAIC YE)は文字の種別としてはひらがなであるが、ブロックとしては仮名補助(Kana Supplement。U+1B000..U+1B0FF)に属す。

Scriptは、その文字がどの書記体系(用字、スクリプト)で用いられるかを表す。ここでは、や行えはHiragana扱いになり、音引きはひらがなとカタカナ両方で使われるためCommonになる。

Script_Extensionsは、複数の値を取れるようScriptを拡張したものである。音引きはここではHiragana, Katakana両方に属する扱いになる。

言語比較

ECMAScript

ECMAScript 2019時点の状況について述べる。

ECMAScript 2018以降の、Unicodeフラグ付きの正規表現で使用できる。

\p{プロパティ名=プロパティ値} の形で指定する。

プロパティ名が General_Category の場合は省略して \p{プロパティ値} と書ける(例: \p{Lu})。

Yes/No の二値を取るプロパティ(バイナリプロパティ)は \p{プロパティ名} と書く(例: \p{Uppercase}\p{Uppercase=Yes} のようには書けない)。

非バイナリプロパティとしては ECMAScript® 2019 Language Specification #Table 54: Non-binary Unicode property aliases and their canonical property names に列挙されているGeneral_Category, Script, Script_Extensions と、その別名であるgc, sc, scx が使用できる(Block は含まれないため、ブロック指定はできない。Blockはnot usefulということでproposal段階で除外された —— Which properties should we include in Unicode escapes? · Issue #18 · tc39/proposal-regexp-unicode-property-escapes)。

バイナリプロパティとしては ECMAScript® 2019 Language Specification #Table 55: Binary Unicode property aliases and their canonical property names に列挙されているプロパティが使用できる。

.NET

General CategoryとBlock指定のみ使用できる。

General Categoryは \p{カテゴリ名} と記述する。カテゴリ名には .NET 正規表現での文字クラス | Microsoft Docs #サポートされている Unicode 一般カテゴリに列挙されている名前が使用できる。 .NET FrameworkのバージョンとUnicode標準のバージョンの対応関係は CharUnicodeInfo クラス (System.Globalization) | Microsoft Docs を参照。

Blockは \p{Isブロック名} と記述する。ブロック名には .NET 正規表現での文字クラス | Microsoft Docs #サポートされている名前付きブロックに列挙されている名前が使用できる。ここで指定できる名前はUnicode 4.0 および Perl 5.6に基づいている。

Java

Java 11時点の状況について述べる。

General Category, Block, Script, バイナリプロパティ指定が使用できる。

General Categoryは \p{カテゴリ名}, \p{Isカテゴリ名}, \p{General_Category=カテゴリ名} と記述する。指定できるカテゴリ名の集合は Characterクラスで指定されたUnicodeバージョンで定義されているカテゴリ名の集合に等しい。

Blockは \p{Inブロック名}, \p{Block=ブロック名} と記述する。指定可能なブロック名は Character.UnicodeBlock#forName(String) の受け付ける名前とされている。UnicodeバージョンはCharacterクラスの指定に準じる。

Scriptは \p{Isスクリプト名}, \p{Script=スクリプト名} と記述する。指定可能なスクリプト名は Character.UnicodeScript#forName(String) の受け付ける名前とされている。UnicodeバージョンはCharacterクラスの指定に準じる。

バイナリプロパティは \p{Isプロパティ名} と記述する。指定可能なバイナリプロパティは Patternクラスのドキュメントに列挙されている。

参考: 鬼雲(Ruby)

鬼雲(Ruby)で使用可能なUnicodeプロパティは https://github.com/k-takata/Onigmo/blob/master/doc/UnicodeProps.txt に列挙されている。

ブロック名は In_ブロック名 の形式、 Ageは Age=バージョン の形式、それ以外は プロパティ値 の形式になっているようだ。

参考: PCRE

PCREの \p で利用可能なプロパティは pcre2pattern(3) で説明されている。

\p{プロパティ値} の形式で、 General Categoryの値、Scriptの値、 Any と、PCRE拡張として Xan, Xps, Xsp, Xwd が指定できる。

UTS #18: Unicode Regular Expressions

UTS #18: Unicode Regular Expressions ver. 19 §1.2 Properties では、正規表現でUnicodeプロパティに対応する際のガイドラインを示している。

構文は以下の通りで、ECMAScriptの \p のスーパーセットになっている。Javaもブロックの \p{Inブロック名} 以外はこの形式だ。

ITEM := POSITIVE_SPEC | NEGATIVE_SPEC

POSITIVE_SPEC := ("\p{" PROP_SPEC "}") | ("[:" PROP_SPEC ":]")

NEGATIVE_SPEC := ("\P{" PROP_SPEC "}") | ("[:^" PROP_SPEC ":]")

PROP_SPEC  := <binary_unicode_property>

PROP_SPEC  := <unicode_property> (":" | "=" | "≠" | "!=" ) VALUE

PROP_SPEC  := <script_or_category_property_value> ("|" <script_or_category_property_value>)*

PROP_VALUE := <unicode_property_value> ("|" <unicode_property_value>)*

\p, \P 以外にPOSIX文字集合形式の [:spec:], [:^spec:] が書けたり、 = の別名として : があったり、 = の代わりに != と書くことで否定が表現できる。また、バイナリプロパティやGeneral Category以外にもScriptプロパティ値も \p{スクリプトプロパティ値} で書けることになっている。

ECMAScriptでは、同一機能の別表記を取り入れるのは実装を複雑にするだけであること、実用上はScriptよりScript_Extensionsの方が有用なため後者を推奨するといった立場からこれらの機能は取り入れなかったようだ。

ECMAScriptはUAX44-LM3のマッチング規則(シンボル値に関して (1) 大文字小文字を区別しない、 (2) -, _, 空白は無視する、 (3) 先頭の is は無視する)も採用していない。