Unicodeで「漢字」の正規表現

正規表現で漢字の範囲指定をする場合、シフトJISでは [亜-熙] になるのですけど、それでは Unicode ではどうするかが悩ましいところです。

[一-龠]([\x{4E00}-\x{9FA0}])にしている例を見かけますが、これは実のところ Unicode の漢字の中から JIS X 0208 の漢字が含まれている範囲を切り出しただけです。これを「Unicode のすべての漢字の範囲」と思っているのでしたら、完全に間違いなので [一-龠] にしてはいけません。

Unicodeのすべての漢字の正規表現 その1

環境によってはUnicodeスクリプトの \p{Han} が使えます。対応するコードポイントの一覧はこちらで確認できます。
UnicodeSet で \p{Han} の対応コードポイント一覧を表示

Unicodeのすべての漢字の正規表現 その2

しかし、Unicodeスクリプトが使えない環境も普通にありますから、この場合はどうしましょうか…? 悩ましい〜

悩まなくていいのは \p{Han} に対応するコードポイントを網羅してしまう方法です。

[\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{3005}\x{3007}\x{3021}-\x{3029}\x{3038}-\x{303B}\x{3400}-\x{4DB5}\x{4E00}-\x{9FC3}\x{F900}-\x{FA2D}\x{FA30}-\x{FA6A}\x{FA70}-\x{FAD9}\x{20000}-\x{2A6D6}\x{2F800}-\x{2FA1D}]

Unicode のバージョンが上がって文字が追加されると、これでも足りなくなるおそれがあるので、それに対応できるよう未定義箇所を含めて指定してみます。ついでに隣接する箇所をなるべくまとめます。

[\x{2E80}-\x{2FDF}\x{3005}\x{3007}\x{3021}-\x{3029}\x{3038}-\x{303B}\x{3400}-\x{4DBF}\x{4E00}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2FFFF}]

これが \p{Han} を網羅する最も簡潔な正規表現でしょうね。

しかし「こんなのヤダ〜!」と悲鳴をあげてしまいますねえ。そこで、日本語のテキスト処理で実用上問題ないところまでさらに少なくしてみます。

そこで「漢字の部首」「ハングルの数字」を除外。

[\x{3005}\x{3007}\x{303B}\x{3400}-\x{4DBF}\x{4E00}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2FFFF}]

もっと減らしたいですねえ。ここで考え方を変えて、分断されている範囲をちょっと強引にまとめてみます。3400..4DBF と 4E00..9FFF の間には「易経の六十四卦」が割り込んでいます。使わない文字にはヒットしないので、無いのと同じと考えて含めちゃいましょう。ついでに1文字しかない箇所はその文字を直接指定します。

[々〇〻\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2FFFF}]

内訳は以下のようになります。

3005 々(漢字の踊り字)
3007 〇(漢数字のゼロ)
303B 〻(漢字の踊り字)
3400..9FFF CJK統合漢字拡張A(+易経の六十四卦)+CJK統合漢字
F900..FAFF CJK互換漢字
20000..2FFFF CJK統合漢字拡張B〜D+CJK互換漢字追加(+念のため)

私としては、これが限界だと思います。もっと減らそうとして 20000..2FFFF を除外すると、人名地名がらみでいつか痛い目に会いそうです。やっぱり悩ましいですね…。

ちなみに [一-龠] はCJK統合漢字を不完全に範囲指定しています。さらに互換漢字ブロックがまるごと含まれていないので、Windows(CP932)のIBM拡張漢字のとりこぼしがあります。簡易な範囲指定だとしても(とくに DTP などでは)かなり危険。私は怖くてできません。

InDesign CS3 以降

[々〇〻\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}\x{020000}-\x{02FFFF}]

「~K」にご不満な方はお試しあれ。(でもちょっと使いづらい…)

JavaScript

[々〇〻\u3400-\u9FFF\uF900-\uFAFF\u20000-\u2FFFF]

これダメです…。\u20000-\u2FFFF がヒットしません。解決方法を教えてください!

(?:[々〇〻\u3400-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF])

えむけいさんが教えてくれました。感謝!

追記

[2011.3.1] 直井さんの InDesign CS3の漢字のメタ文字「~K」を検証する をつらつら見ていたところ、「括弧付き漢字」と「丸付き漢字」もあることに気づきました。あらー私はすっかり見落としていましたよ。で、これを加えると

[々〇〻\x{3220}-\x{3244}\x{3280}-\x{32B0}\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2FFFF}]

ますます使いづらいことに…。でも、この「括弧付き漢字」「丸付き漢字」は \p{Han} に含まれていないんですよねぇ、あらら。

11件のコメント

  1. JavaScriptの文字列は16ビットUnicodeで表現される仕様になっており、BMPに含まれない文字はサロゲートペアで表現されます。このため、/\u20000-\u2FFFF/ ではなく /[\uD840-\uD87f][\uDC00-\uDFFF]/ としないとヒットしないのです。
    参照:http://liosk.blog103.fc2.com/blog-entry-72.html あたり

  2. [々〇〻\u3400-\u9FFF\uF900-\uFAFF\U00020000-\U0002FFFF]

    InDesign CS3 の JavaScript で試したら、↑これでヒットしました。
    いいのかな? 大丈夫かな? 自分で使いながら様子を見てみます。
    ちなみに仕事では「★愛★あい★」といったルビ仕込みのテキストを InDesign 上で選択する JavaScript を作って使ってます。なので漢字の正規表現が必要なのです。

  3. \UhhhhhhhhはInDesignの独自拡張みたいです。Webブラウザ(少なくともIE8, Firefox 3.6.4build1, Opera 10.52 for Win, Safari 4.0.5 for Win, Chrome)ではサポートされされていませんでした。
    ECMA-262の仕様では文字列は単なる符号なし16bit整数の配列ということになっていて、サロゲートペアについても何もしてくれません。自分でエンコードする必要があります。
    (?:[々〇〻\u3400-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF])

  4. えむけいさん、検証とコメント、ありがとうございます!

    > \UhhhhhhhhはInDesignの独自拡張みたいです。
    いくら調べてもこれでいいという情報がないので心配でしたが、
    独自拡張ということなら大丈夫そうですね。
    (念のため、時間が空いたときにもうちょっと検証してみます)

    > (?:[々〇〻\u3400-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF])
    なるほど〜「前後のコードポイントの組み合わせ」にするんですね!

  5. あ〜〜! はげしく勘違いしてた!!
    私が InDesign でやっていたのは↓コレでした。

    var reg_Oyazi = “[々〇〻\u3400-\u9FFF\uF900-\uFAFF\U00020000-\U0002FFFF]+”;
    var reg_Rubizi = “[  ぁ-ヿ*]+”;
    var my_find_obj = {findWhat:”★”+reg_Oyazi+”★”+reg_Rubizi+”★”};

    まさに独自拡張…

    で、これで選択した箇所を JavaScript でルビにするのに親字の文字数をかぞえる必要があって、そうするとサロゲートペアが邪魔なので別の文字に置換しなくちゃいけなくて、

    oyazi = oyazi.replace(/(?:[\uD840-\uD87F][\uDC00-\uDFFF])/g,”親”);
    var oyaziCount = oyazi.length;

    これでバッチリ。えむけいさん、ありがとう!

  6. anony さん
    「ハングル語」は間違いで「ハングル」が正しいということですか。
    直しましたー

  7. 先日「~K」を使って失敗しました。もっとはやくこの記事に出会っていれば……。次回からはこちらの正規表現を使わせて頂きます。貴重な情報ありがとうございます。

  8. 有用な情報、ありがとうございます。
    本論とは関係ない部分ですが、易経は四十六卦ではなくて、六十四卦ではないでしょうか。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です