文字列が「中国語かどうか」を判定したい
漢字だけの文字列を渡して、「日本語」なのか「中国語」なのかを判別するのって、かなり難しかった…。
文章がとっちらかっているが、WIP なライブラリの話をしているので、ご容赦をば…。
目次
- 外国語を判定してみたくなった
- ES2018 の Unicode Property Escapes という新機能を知る
- 「中国語」と「日本語」は Unicode ブロックが別れていない
- Unihan データベースを使って「中国語で使われる漢字」を判定する
- 「中国語を判定するプログラム」を作成中
- 判定漏れがある
- ご意見・アドバイス等ください
外国語を判定してみたくなった
特に目的はないのだが、ふと思いついて、ある文字列が外国語の文章かどうかを判定できる正規表現を書きたいなーと思った。「コレはロシア語だ」とか「コレはハングルだ」みたいな。
Unicode の「ブロック」を見ると、ロシア語に使われる「キリル文字」や、韓国語の「ハングル」、「アラビア文字」などは、ブロックが綺麗に別れているので、この Unicode ブロックの範囲で正規表現が書けそうだと気付いた。
なお、ハングルについては以前「ハングルを特定する正規表現」を見つけていて、コレがこの Unicode ブロックにおける、「Hangul Syllables ハングル音節文字」(U+AC00 ~ U+D7AF) にほぼ相当することが分かった。
- 過去記事 : YouTube ホームの見たくない動画を消すための術
- ハングルを判定する正規表現 | You Look Too Cool
[가-힣]
コレでハングル文字を含む動画を非表示にしたりしてる
가
が U+AC00、힣
が U+D7A3
以下にデモページとコードを用意してみた。
- デモ : Detect Languages RegExp
- コード : frontend-sandboxes/index.html at master · Neos21/frontend-sandboxes
ES2018 の Unicode Property Escapes という新機能を知る
さらに調べて行くと、ES2018 で Unicode Property Escapes という機能が追加されていたのを知った。JS の RegExp
で /\p{Script_Extensions=Hangul}/u
と書くことで「ハングルに合致する正規表現」を簡単に書ける、という仕組みだ。
- 参考 : ES2018で入ったUnicode property escapes in regular expressionsを調べてみた - yutaponのブログ
- 参考 : JavaScriptのES2018で追加された正規表現の新機能の検証 - Qiita
- 参考 : Unicode property escapes in regular expressions
Unicode Property Escapes というのは他のプログラミング言語には既に実装されていて、例えば PHP や Ruby なんかでも、\p{Han}
と書くことで「漢字」にマッチする正規表現を簡単に書けるようだ。Unicode コードポイントを大量に列挙したりしなくて良いワケである。
他言語の \p{Han}
(Han というのは「漢」のこと) 相当の正規表現を Unicode コードポイントで記そうとすると、以下のようになる。
const HAN_REGEX = /[\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DB5\u4E00-\u9FD5\uF900-\uFA6D\uFA70-\uFAD9]/;
コレが、ES2018 以降では
const HAN_REGEX = /\p{Script=Hani}/u; // 2つの正規表現は同じ
const HAN_REGEX = /\p{sc=Han}/u; // コチラは省略記法
と、このように書けるワケである。
「中国語」と「日本語」は Unicode ブロックが別れていない
さて、「キリル文字」や「ハングル」なんかは、Unicode ブロックでの範囲指定なり、それを上手くグループ化した Unicode Property Escapes という機能でほぼほぼ判定できそうな様子が分かってきた。日本語に関しても、ひらがなとカタカナについては Unicode Property Escapes が存在する。
しかし、漢字の中でも「中国語」を判定してみようと思った時、壁にブチ当たった。
Unicode の界隈でよく CJK、「CJK 統合漢字」という言葉を耳にしていた。中国語・日本語・朝鮮語で使われる漢字をひとまとめにした符号表である。
- 参考 : CJK統合漢字 - Wikipedia
日本語と中国語をひとまとめに…。確かに、日本語の中で使う漢字の多くは、中国の漢字を流用しているので、「CJK」といっしょくたにまとめられているワケだ。しかしそうなると、漢字に関しては、キリル文字やハングルみたいに「この文字がマッチしたからロシア語・韓国語ね!」という簡単な判定ができないことになる。
つまり、膨大な CJK 統合漢字の中から、
- 「日本語において使用される漢字」と、
- 「中国語の中でのみ使用され、日本語では使用されない漢字」
とを何とか区別してやらないと、「この文章が中国語かどうか」を判定するプログラムは書けないということだ。
Unihan データベースを使って「中国語で使われる漢字」を判定する
さらに調べていくと、次のページを見つけた。
何やら、Unihan : Unicode Han Database という漢字に関するデータベースがあるらしい。データベースと言いながら実体はテキストファイルなのだが、Unicode コードポイントごと (= 漢字ごと) に「日本語の音読み・訓読み」だとか「北京語・広東語での読み方」だとかを記しているようだ。
- 参考 : Unihan Database Lookup
- 参考 : Index of /Public/UNIDATA
- Unihan.zip
- ↑ コレがそのデータベースとなるテキストファイルが格納された Zip
また、この Unihan データベースを使って、繁体字と簡体字を区別するような記事もいくつか見かけた。
ココに来て、「中国語」とは何か、ということになるのだが、中国語を勉強したことがないので、よく分かっていない。
ただ、今回僕が考える目的からすると、前述のとおり
- 日本語で使用する漢字だけを含んだ文字列だったら「それは日本語である」と判定し、
- 日本語で使用しない漢字を含んでいる文字列だったら「それは中国語である」と判定したい
ということにする。
そういうワケで、先程の参考ページを参考に Unihan データベースを使ってみることにした。
「中国語を判定するプログラム」を作成中
以下の GitHub リポジトリに、作成したコードを置いている。
Node.js で require()
してもいいし、HTML から読み込めばブラウザ上でも動作するし、npm install
すれば $ detect-chinese
コマンドとして動作するような CLI 環境も簡単に用意してみた。以前の記事で少し紹介し、@neos21/ccc
でも採用している UMD 形式でコードを書いている。
Unihan データベースの Unihan_Readings.txt
というファイルから
- 「日本語の音読み・訓読みを持つ漢字」リストと
- 「北京語・広東語読みを持つ漢字」リスト
を抽出し、Unicode コードポイントを列挙して RegExp
を定義するところまではできた。まぁ巨大な正規表現になった…。
そしてその後の判定についても、「日本語の音訓読みを持たず、中国語読みを持つ漢字」を特定したりして、ある程度は「中国語でのみ使われる漢字を含んだ文章かどうか」を判定できている。
判定漏れがある
一部、既知の問題として、「つちよし 𠮷
」の文字が日本語とも中国語ともみなされない文字として扱われてしまっている。
- 一見すると「
吉
と同じく、日本語の中で使われる漢字」っぽく見えるのだが… - 原因は、「つちよし
𠮷
」の文字が漢字リストの生成に使ったUnihan_Readings.txt
ファイルに定義されていないため - 「つちよし
𠮷
」の文字は「スプーフィング異体」という異字体の扱いで、別のデータベースファイルUnihan_Variants.txt
の方で吉
との対応付けが定義されている
そういうワケで、判定漏れしている漢字もまだまだありそうなことが分かっている。
「つちよし 𠮷
」は「日本語漢字」に含めても良いかも知れないが、異字体は他にも沢山あり、それらについては「日本語漢字」とみなしていいのかどうかなど、もう少し考えないといけないところが多そうだ。
ご意見・アドバイス等ください
中国語をロクに知らないのに、ワケもなく中国語を判定したくなった程度の人間が作っているので、こんな感じで精度はボチボチ。通常の文章なら大抵は「中国語か日本語か (それ以外か)」を判定できているが、もう少しなんとかしていきたい。中国語の有識者のご意見を聴きたい。
最終的に npm パッケージとして配信して汎用的に使えるようにしたいなーと思っているので、どういう機能をライブラリとして提供できれば良いか、要望やアドバイス等あればいつでもご連絡ください。
↑ ご意見がありましたら、GitHub リポジトリの GitHub Issues に起票していただけますと嬉しいです。
興味が続けば今後も改修していきます…。笑