[正規表現] Unicode 文字プロパティ (02) -- 数字表現

立て続けに第2回です。

# 連載目次

- [第1回 -- 概要](http://qiita.com/hachi8833/items/22d21d03827dfd5fbed1)
- 第2回 -- 数字表現

# 数字の扱い

Unicode 文字プロパティで数字を表す[カテゴリ](http://www.unicode.org/reports/tr44/tr44-16.html#GC_Values_Table)には以下の3つがあります:
- [Number, Decimal unit](http://www.fileformat.info/info/unicode/category/Nd/list.htm): いわゆる数字。`Nd`で表す。
- [Number, Letter](http://www.fileformat.info/info/unicode/category/Nl/list.htm): ローマ数字（Ⅷとか）など。`Nl`で表す。
- [Number, other](http://www.fileformat.info/info/unicode/category/No/list.htm): その他の数字(⅔など)。いわゆる機種依存文字(⓴など)の多くもここに掃き寄せられている。`No`で表す。

文字プロパティでは大文字と小文字を区別しますのでご注意ください(`Nl`と`NL`は違います)。処理系によっては動くこともあるようですが、将来改定される可能性もあるので区別しておくことをお勧めします。

今更ですが、正規表現の中で使用する際は`[\p{Nd}]`のように文字クラスを表す`[]`で囲みます。`[]`で囲まなくても動作する言語もあったりしますが、将来改定される可能性もあるので、極力囲んでおくことをお勧めします。

3つのカテゴリすべてを指定するには、`[\p{Nd}\p{Nl}\p{No}]`としてもよいですが、等価表現である`[\p{N}]`と書くのが簡潔です。

## 漢数字は数字か

Unicode文字プロパティでは漢数字は数字として扱われるのでしょうか、漢字として扱われるのでしょうか。

![01-003.png](https://qiita-image-store.s3.amazonaws.com/0/20266/baa037d9-97aa-9b79-2c42-6c92a09866f0.png)
[文字クラス「L」でマッチ](http://rubular.com/r/ui3nNXrqC9)
![01-004.png](https://qiita-image-store.s3.amazonaws.com/0/20266/447282dc-dfba-a9d8-825c-6d9ec4ade8c8.png)
[文字クラス「N」でマッチ](http://rubular.com/r/jKTLJcdW60)

やってみると、漢数字は「文字」を表す`[\p{L}]`にマッチし、「数字」を表す`[\p{N}]`にはマッチしていません。普通に考えてこうするしかないでしょうね。

が、見てお分かりのとおり、ゼロを表す`〇`だけは数字として認識されています。要注意です。そもそも`〇`は漢字なのか数字なのかということが微妙ですが。

## 意外なものが数字として扱われる

冒頭のリンクをクリックして`Nd`、`Nl`、`No`の文字の実例を見てビビビッと嫌な予感がした方もいらっしゃると思います。
そのとおり、日本人が日本のお客さんに頼まれて作るコードで使用する正規表現であれば、数字の表現はいつもの`[0-9]`とか`[０１２３４５６７８９]`みたいなのにしておくのがよいでしょう。数字とマッチする表現に`[\p{N}]`を使うと、マッチ範囲が恐ろしく広いので思わぬ動作をする可能性が高いです。

つまり、`[0-9]`と`[\p{N}]`は**目的がまったく違う**と考えるのがよろしいと思います。

(`\d`とかはどのぐらい普遍性があるのか知らないので、自分は`[0-9]`にしています。)

[Nd](http://www.fileformat.info/info/unicode/category/Nd/list.htm)だけでもアラビア数字(いわゆる`[0-9]`ではなく本当にアラビア語で使われている数字、微妙に形が違う)を始めとする世界のさまざまな言語の数字が含まれています。

さらに、数学で特別な意味に使うドイツ式数字「𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡」やボールド、サンセリフなどの数字までわざわざコードとして用意されています。
![02-001.png](https://qiita-image-store.s3.amazonaws.com/0/20266/afb429b0-01ba-f0e2-96b1-d66f4398fd82.png)
こういうのはレイアウト/レンダリング側で対応すべきことであって、文字コード体系側でやることではない気がして仕方がないのですが、何か事情があるのかもしれません。

続く[Nl](http://www.fileformat.info/info/unicode/category/Nl/list.htm)にしても、数字なのか文字なのか、ネーミングからして怪しさが漂います。
ローマ数字の10や100や1000を表すⅭやⅮやⅯにわざわざコードを割り当てています。
![02-002.png](https://qiita-image-store.s3.amazonaws.com/0/20266/598ff281-b139-c5c6-1c5e-23253b9f032a.png)

文字で数字の代用にするというのは古代ギリシャからローマにかけてずっとやってきたことなのである程度仕方がないとはいえ、わざわざコードをこしらえる意義がよくわかりません。

もっとも逆も真なりで、[CJK統合漢字](https://ja.wikipedia.org/wiki/CJK%E7%B5%B1%E5%90%88%E6%BC%A2%E5%AD%97)で中国・朝鮮・日本の漢字を一部で同一視したことで議論が巻き起こったりしています。詳しくは知りませんが。

それなら、漢字の「十百千万億兆」を`Nl`にも含めて欲しいような気もします。しかし簡単な話ではなさそうです。

## カテゴリはひとつのコードにひとつ

仕様をちゃんと探していませんが、これまで見た限りではUnicode文字コード体系で使用されているカテゴリは、1つのコードにつきひとつしか与えられていないようです。マルチカテゴリのコードは今のところ見当たりません。きっとそうであろうと仮定してひとまずメモします。

ひとつは、複数のカテゴリに属するコードを作らないことで、正規表現で利用しやすくする意図があるのだと思います。正規表現の中でUnicode文字プロパティのカテゴリを使用する場合、当然ながらなるべく余分なものは混入しないで欲しいはずです。「文字だけが欲しい」ときに「数字も文字だから」とマッチされては困ります。

もうひとつは、マルチカテゴリ化を行うとなるとカテゴリの割り当て作業が膨大になってしまうからではないでしょうか。作業のためには全言語でのチェックが必要になりますが、これはエンジニアだけで行える作業ではありません。国連の職員並に、きわめて多くの自然言語に精通した人物が必要です。

もしかすると水面下で進行中なのかもしれませんが、マルチカテゴリに変わったら変わったで非常に大きな影響が生じます。たぶん今更変えられない気がしてきました。既存のカテゴリを変更するのは無理でも、新しくカテゴリを追加するならいけるかもです。

# 追記

`[0-9]`は半角数字にマッチしますが、全角数字「０１２３４５６７８９」にはマッチしません。少なくともRuby + Onigmo では。http://rubular.com/r/YAfmbxtkmB

当然、否定形`[^0-9]`は全角数字にマッチします。

しかしたまたま知ったのですが、NSPredicate では否定の`[^0-9]`が全角数字にマッチしないのだそうです。ということはNSPredicateでは肯定の`[0-9]`は全角数字にマッチしてしまうのでしょう。自分で動かしてないのでわかりませんが。