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

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

# 概要

下表のように、ECMAScript, .NET (C#), Javaでは `\p{カテゴリ名}` 形式のGeneral Cateogry指定以外の記法は三者に互換性がない。
 
|Unicodeプロパティ | ECMAScript | .NET | Java |
|:--|:--|:--|:--|
General_Category|\p{Lu} <br/>\p{General_Category=Lu} <br/>\p{gc=Lu}|\p{Lu}|\p{Lu}  <br/>\p{IsLu} <br/>\p{General_Category=Lu}<br/>\p{gc=Lu}|
|Block|-|\p{IsMongolian}|\p{InMongolian} <br/>\p{Block=Mongolian} <br/>\p{blk=Mongolian}|
|Script|\p{Script=Common} <br/>\p{sc=Common}|-|\p{IsCommon} <br/>\p{Script=Common} <br/>\p{sc=Common}|
|Script_Extensions|\p{Script_Extensions=Adlam} <br/>\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](http://www.unicode.org/reports/tr44/tr44-24.html#General_Category_Values)はコードポイントの最も一般的な分類を表す。ひらがなやカタカナは、大文字小文字といった区別のない文字なのでLo（Letter Other）カテゴリに属し、音引きは他の文字のあとについて長音を表す文字なのでLm（Letter Modifier）カテゴリに属す。

[Block](http://www.unicode.org/reports/tr44/tr44-24.html#Block)は連続する符号位置の範囲に名前をつけたものである。や行え（HIRAGANA LETTER ARCHAIC YE）は文字の種別としてはひらがなであるが、ブロックとしては仮名補助（Kana Supplement。U+1B000..U+1B0FF）に属す。

[Script](https://www.unicode.org/reports/tr24/tr24-29.html)は、その文字がどの書記体系（用字、スクリプト）で用いられるかを表す。ここでは、や行えはHiragana扱いになり、音引きはひらがなとカタカナ両方で使われるためCommonになる。

[Script_Extensions](https://www.unicode.org/reports/tr24/tr24-29.html#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](https://www.ecma-international.org/ecma-262/10.0/index.html#table-nonbinary-unicode-properties) に列挙されている`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](https://github.com/tc39/proposal-regexp-unicode-property-escapes/issues/18#issuecomment-261256329)）。

バイナリプロパティとしては [ECMAScript® 2019 Language Specification #Table 55: Binary Unicode property aliases and their canonical property names](https://www.ecma-international.org/ecma-262/10.0/index.html#table-binary-unicode-properties) に列挙されているプロパティが使用できる。

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

General Categoryは `\p{カテゴリ名}` と記述する。カテゴリ名には [.NET 正規表現での文字クラス | Microsoft Docs #サポートされている Unicode 一般カテゴリ](https://docs.microsoft.com/ja-JP/dotnet/standard/base-types/character-classes-in-regular-expressions#SupportedUnicodeGeneralCategories)に列挙されている名前が使用できる。 .NET FrameworkのバージョンとUnicode標準のバージョンの対応関係は [CharUnicodeInfo クラス (System.Globalization) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.globalization.charunicodeinfo) を参照。

Blockは `\p{Isブロック名}` と記述する。ブロック名には [.NET 正規表現での文字クラス | Microsoft Docs #サポートされている名前付きブロック](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/character-classes-in-regular-expressions#SupportedNamedBlocks)に列挙されている名前が使用できる。ここで指定できる名前はUnicode 4.0 および Perl 5.6に基づいている。

## Java

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

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

General Categoryは `\p{カテゴリ名}`, `\p{Isカテゴリ名}`, `\p{General_Category=カテゴリ名}` と記述する。指定できるカテゴリ名の集合は [Character](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Character.html)クラスで指定されたUnicodeバージョンで定義されているカテゴリ名の集合に等しい。

Blockは `\p{Inブロック名}`, `\p{Block=ブロック名}` と記述する。指定可能なブロック名は [Character.UnicodeBlock#forName(String)](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Character.UnicodeBlock.html#forName(java.lang.String)) の受け付ける名前とされている。UnicodeバージョンはCharacterクラスの指定に準じる。

Scriptは `\p{Isスクリプト名}`, `\p{Script=スクリプト名}` と記述する。指定可能なスクリプト名は [Character.UnicodeScript#forName(String)](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Character.UnicodeScript.html#forName(java.lang.String)) の受け付ける名前とされている。UnicodeバージョンはCharacterクラスの指定に準じる。

バイナリプロパティは `\p{Isプロパティ名}` と記述する。指定可能なバイナリプロパティは [Pattern](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/regex/Pattern.html)クラスのドキュメントに列挙されている。

## 参考: 鬼雲（Ruby）

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

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

## 参考: PCRE

PCREの `\p` で利用可能なプロパティは [pcre2pattern(3)](https://www.pcre.org/current/doc/html/pcre2pattern.html) で説明されている。

`\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](http://unicode.org/reports/tr18/tr18-19.html#Categories) では、正規表現で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では、[同一機能の別表記を取り入れるのは実装を複雑にするだけ](https://github.com/tc39/proposal-regexp-unicode-property-escapes#why-not-support--as-a-separator-in-addition-to-)であること、[実用上はScriptよりScript_Extensionsの方が有用なため後者を推奨する](https://github.com/tc39/proposal-regexp-unicode-property-escapes#why-not-support-eg-pscriptname-as-a-shorthand-for-pscriptscriptname)といった立場からこれらの機能は取り入れなかったようだ。

ECMAScriptは[UAX44-LM3](https://unicode.org/reports/tr44/#UAX44-LM3)のマッチング規則（シンボル値に関して (1) 大文字小文字を区別しない、 (2) `-`, `_`, 空白は無視する、 (3) 先頭の `is` は無視する）も採用していない。