Unicode と異体字・傾向と対策

tl;dr

  • Unicode で異体字(ちょっと変わった字形の漢字とか)を扱うときは気をつけよう
    • CJK 互換漢字が含まれていたらふとしたタイミングで字形が変わることがあるよ
    • そういうケースでも、SVS 使えばたぶん解決するよ
  • それ以前に IME で目当ての字形が出せないって? その悩み、IVS で解決するかもしれないよ
    • 使用コレクションとフォントには注意が必要だよ
  • 苦悩するワタナベさん

こんにちは、坂井です。Unicode の話をします。なぜなら坂井は異体字が好きだからです。先日某 SNS じゃなくてマイクロブログなサービスにうっかり CJK 互換漢字を投下し、正規化されて泣きました。何を言っているんだと思われるかもしれません。しかし今回の記事はそういう話です。ざっくばらんには、変な漢字、難しい漢字、旧い漢字などをプレーンテキストでできるだけ安全に扱いたくてお勉強をした、という内容です。「役場や郵便局などで手続きしようとしたら名前の漢字の確認をされた」とかいう経験のある方にもおすすめかもしれません。

以降、面倒くさいので適当な「である」調でいきます。

そもそも Unicode における漢字の扱いとは 〜お前ら似てるから一緒でいいよね〜

いろいろ調べているうちに「あ、まず Unicode における漢字の立ち位置がわからんとどうにもならん」ということに気が付いたので、まず Unicode Standard やその周辺文書をざーっと読んでみた。とりわけ異体字に関して根っこになる部分は漢字の統合 (Han Unification)、さらに言うなら「漢字はどのようにして Unicode に収録されるのか」という部分と思われるので、それについてざっくり触れる。

Unicode では意味 (X軸: semantic)・抽象的な形状 (Y軸: abstract shape)・書体 (Z軸: typeface) の 3 軸からなる 3 次元モデルを用意し、それぞれの漢字がどの座標に位置するかというのを見ている。UAX #38 にわかりやすい具体例が挙げられていて、例えば 貓 と 猫 はどちらも意味と発音が同じ(発音同じなのか?)だけれど抽象的な形状が違うので、X 座標は等しいが Y 座標が異なる (y-variants)。說 と 説 は意味も発音も、抽象的な形状も同じだが、stylistic variation(異体字)であって Z 座標が違う (z-variants)。本当は z-variants なペアは Unicode に存在するべきではないが、登録ミスなどが原因で存在してしまっているのが実情らしい。

The Unicode Standard, Version 13.0 - Principles of Han Unification
UAX #38: Unicode Han Database (Unihan) - 3.7 Variants

そう、z-variants なペア、すなわち「この字はあの字の異体字」という関係性は基本的に Unicode にあってはならないという。「えぇ?」と思った方も多いのでは。特に、そこのワタナベさん。だがしかし、少しの間辛抱して聞いてほしい。

Unicode に収録されている漢字は、漢字圏各国が持ち寄った漢字セットをいい感じにアレしてソレしたものである。

Unicodeは漢字統合の提案をISO/IEC 10646に持ち込み、中国・日本・韓国の共同作業部会(CJK-JRG)において作業が行われた。この作業の成果として1992年に最初のCJK漢字統合(Unified Repertoire and Ordering (URO))ができあがった。UROは中国・日本・韓国の地域別標準文字コード表を出典とするが、元になる文字コード表で別のコードになっていれば統合しないという出典分離規則がある。UROはUnicode V1.0.1で20,902文字が規定され、その後、48文字が追加されている。また、出典文字コード表と双方向変換するための文字や出典外の主要文字として互換漢字が302文字規定された。その後、互換漢字は170文字追加されている。
JEPA|日本電子出版協会 Unicodeとは?

漢字圏各国は漢字セットを持ち寄ったが、当然セット間で漢字の重複があったり、「ほぼ同じじゃね?」みたいなのがあったりする。その重複や「ほぼ同じ」漢字を Unicode 収録時に一緒くたにするために、漢字の統合ルールが定められている。やや本題から外れるのでざっくばらんに紹介すると、

  • 原規格分離ルール (Source Separation Rule)。出典で別の漢字とされているなら Unicode でも統合しない。
    • CJK 統合漢字 (CJK Unified Ideographs、漢字集合の代表格) の初版にのみ適用。このルールのため、今なら統合されるような漢字も別々に収録されているケースがある。
  • 非同系ルール (Noncognate Rule)。歴史的起源の観点で無関係な漢字は統合されない。
    • Unicode Standard には 土 (earth) と 士 (warrior, scholar) が「似てるけど歴史的に無関係だし意味も違う」と例示されている。
  • 抽象的な形状が同じ、かつ上記 2 ルールに引っかからないなら、その 2 字は統合される。
    • 「抽象的な形状が同じ」の定義についてはここでは触れないので、気になる方は Unicode Standard を読んでください。

どうですか、「ナベ」の字が難しいタイプのワタナベさん。ゾワゾワしてきませんか。「難しいナベ」、原規格分離ルールに引っかからない限り「お前ら z-variants な!!!」と漢字統合でゴリゴリ潰されかねません。そして恐らく、CJK 統合漢字(拡張含む)に収録されているのは U+8FBA (辺) と U+9089 (邉)、U+908A (邊)、あとつよつよフォントじゃないと表示できなさそうな U+2E7BD。この世にはこんなにも多くの「難しいナベ」が存在するというのに。

u8fba (辺) - GlyphWiki

ああ、バリエーション豊かなワタナベさんたちはどうしたら救われるのだろう。

閑話休題。最終的には近いところに行き着くにしても、この記事の本題はそこではない。

CJK 互換漢字とは 〜一緒じゃないんだなコレが〜

上の JEPA からの引用でも触れられているが、CJK 互換漢字 (CJK Compatibility Ideographs) というものがある。

「互換」というのは、漢字を Unicode に収録するときに参照した出典元と Unicode との間の互換性のこと。何らかの事情があって「この漢字を収録しておかないと、元の文字セットとのラウンドトリップ変換ができなくなっちゃう」という状況になっているが、さりとて CJK 統合漢字には入れられない、だが互換性は確保したい、そんな漢字たちが CJK 互換漢字という集合になっている。事情というのはモノによりけり、例えば韓国の文字コード規格 KS X 1001:1998 には「字形は同じだけれど発音が違う」という文字がたくさんあって、字形が同じなので Unicode 的には 1 字としたいが、でもそうすると元ネタと対応が取れなくなる……という理由で字形重複分が CJK 互換漢字に入っている。日本出身の CJK 互換漢字には JIS X 0213:2000 との互換性確保のためのものなどがいる。

この漢字たちはその性質上、字形が似ていたり、一緒だったりという兄弟が CJK 統合漢字にだいたいいる(一部例外あり)。でなければ普通に CJK 統合漢字に収録されていただろうし。HTML の対応表がここにある(フォントによっては正しく表示されないかもしれない)。

CJK Compatibility Ideographs - unicode.org

このように一応 CJK 漢字の一員ではあるものの、無闇矢鱈と使っていいコードポイントではないらしい。せっかく大義名分を得て Unicode に収録された異体字(や見た目は同じ字)であるのに、これらはあくまで互換性を保つためのものであって、との由。なので、ひょんなタイミングで Unicode 正規化(ざっくばらんには、検索性などの担保のために「表記揺れ」ならぬ「コードポイント揺れ」などを正す処理)が走ると CJK 統合漢字に寄せられてしまったりする。たとえ字形が多少違ったとしても。せっかくその字形として収録されているのに!

Unicode 正規化という嵐 〜いいや、お前たちは一つになる運命〜

具体的にどのようにいじられてしまうのか? CJK 互換漢字については各漢字の kCompatibilityVariant プロパティに「兄弟」のコードポイントが格納されていて、それに寄せられる。UnicodeData.txt(セミコロン区切りでいろいろデータが詰め込まれているやつ)では Field 5 が kCompatibilityVariant にあたる。

The Unicode Standard, Version 13.0 - 2.3 Compatibility Characters
The Unicode Standard, Version 13.0 - 5.19 Mapping Compatibility Variants

kCompatibilityVariant
The canonical Decomposition_Mapping value for the ideograph, derived from UnicodeData.txt. This field is derived by taking the non-null Decomposition_Mapping values from Field 5 of UnicodeData.txt, for characters contained within the CJK Compatibility Ideographs block and the CJK Compatibility Ideographs Supplement block.
UAX #38: Unicode Han Database (Unihan)
UnicodeData.txt (unicode.org)

試しに CJK 互換漢字として収録されている U+FA10 (塚) を見てみよう。「点の付いた塚」である。全国の大塚さんよご照覧。

FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;;

U+585A (塚) に化けるようだ。点が!!! 点が消えました!!!!! これは困った!!!!!!!

というように、「いろいろ事情があって CJK 互換漢字として収録されている」漢字はひょんなことから正しい字形を失ってしまうことがある。これが冒頭で嘆いていた「某 SNS 云々」の詳細である。確かに文字列比較などの観点からは正しいかもしれない(現に自然言語処理分野なんかではデータの前処理としてポピュラーらしい)。とはいえ、いかに Unicode さんが「成り立ちが同じならそれは同じ字だよ」としたり顔で頷いているとしても、その字形をどうしても保てないと困る、というケースはやっぱりある。この通り人名とか。あるいは使われている字形そのものが重要な情報であるような、歴史的資料であるとか。

SVS という救い 〜すずと、ことりと、それからわたし〜

やはりよろしくない、となったのか、2013 年に制定された Unicode 6.3 で CJK 互換漢字に対して Standardized Variation Sequence (SVS) が利用できるようになった。

The Unicode Standard, Version 13.0 - 23.4 Variation Selectors
StandardizedVariants.txt (unicode.org)

これは何かというと、異体字セレクタ (Variation Selector) なる特殊な文字を使って「この字はあの字のこのバリエーションですよ」というのを指定する仕組みのうち、Standardized Variants という対応表を使うものを指す。つまり CJK 統合漢字に異体字セレクタを添えて「この CJK 統合漢字のあの異体字」と示すことで、CJK 互換漢字にしか収録されていなかった字形も表せるようになったのだ! これも StandardizedVariants.txt を読むととてもわかりやすい。先程の「点の付いた塚」こと U+FA10 は次のように記述されている。

585A FE00; CJK COMPATIBILITY IDEOGRAPH-FA10;

585A はともかく、FE00 というのがくっついている。PHP で出してみよう……。

1
echo mb_chr(hexdec('585A'), 'UTF-8') . mb_chr(hexdec('FE00'), 'UTF-8');

点付きの塚って、点の長さのバリエーションもあるのだよね……。

すばらしい! この FE00 というのが異体字セレクタ、乱暴な喩えをするなら濁点のようなものといえるだろうか(メチャクチャ乱暴なのでマサカリを投げないで)。あくまで Unicode 上ではコードポイントを割り当てられた「文字」もどきなので、このように文字列連結でいい感じになるらしい。ちなみに異体字セレクタブロックのうち、CJK 互換漢字用には FE00-FE02 が割り当てられている。3 つあれば事足りたのかしらん。

ちなみにここで挙げた 3 種類の塚、雑に VSCode で検索してみるとこんな感じになる。

585Aの塚で一致を見る
※フォント(たぶん Osaka)に「点付きの塚」が収録されていないので、表示上全部点が消えている

異体字セレクタ版がなにやら面妖だけれど、納得は納得。2 字で 1 字みたいなものだし。文字列検索についてはアプリケーションによって挙動が違うので要注意だったりする。

それはさておき、つまり、今やこの SVS を利用すればそれなりに安全に CJK 互換漢字で収録されたような字形が表現できる。じゃあ一件落着、と言いたいのだけれど、皆さん、コピペするときにいちいちコードポイントとか気にします? 点付きの塚が 塚 なのか 塚︀ なのか、いちいち気にするには人生は短すぎるし(※ U+FA10 と U+585A FE00(逆だったかもしれねェ……))、コピペじゃなくとも、過去のテキストファイルがどうなってるかとか、そもそも UTF-8 じゃなかったらとか、考慮していたらとてもとてもハゲるどころか死んでしまう。そして知らないうちに Unicode 正規化の嵐に巻き込まれて、意図されていた字形を失ってしまう、と。これは不幸です。割と真面目に文化の危機です。某 Web サービスで投稿時にとか、某 DTP ソフトでどうこうとか、割とそのへんに Unicode 正規化の罠はあるという(ちゃんとした裏を取っていないのでぼかしています)。ちょっと変わった漢字を扱うとき、あるいは絶対に間違えてはいけない漢字を扱うときには少し気にしたほうがよいのかもしれませんね、という話でした。

ところでワタナベさんが救われていない。

ワタナベさんに贈る IVS の話

ここまでは「CJK 互換漢字という形で Unicode に収録されている割に不遇な連中」の話だった。ワタナベさんが救われなかったのは「難しいナベのほとんどは、そもそも CJK 互換漢字にすら入っていない」からだ。しかしどうだ、「字形が似てればだいたい同じ字っしょ」と言っていた Unicode は俄に異体字に対しやる気を出したんじゃないか。

ここに Ideographic Variation Database (IVD) というものがある。

The Ideographic Variation Database provides a registry for collections of unique variation sequences containing ideographs, allowing for standardized interchange according to UTS #37, Unicode Ideographic Variation Database.
Ideographic Variation Database
UTS #37: Unicode Ideographic Variation Database

そう、漢字のための異体字セレクタは Standardized Variants で定められたものだけではない(というか IVD の方が古い)! IVD の対応表 (IVD_Sequences.txt) を使って行う異体字シーケンスは Ideographic Variation Sequence (IVS) という。

IVD_Sequences.txt (unicode.org)

IVD には様々な団体が自分のところで作った コレクション を提供していて、IVD_Sequences.txt にもどのコレクションが出典なのか明記されている。日本語ユーザが使いそうなコレクションは以下の 3 つ。

Adobe-Japan1: adobe-type-tools/Adobe-Japan1: The Adobe-Japan1-7 Character Collection
Hanyo-Denshi: 汎用電子情報交換環境整備プログラム
Moji_Joho: 文字情報基盤整備事業 | 一般社団法人 文字情報技術促進協議会

Moji_Joho は Hanyo-Denshi の後継のようなので、事実上 Adobe-Japan1 と Moji_Joho の 2 つと言えるかもしれないし、後述する対応フォントの問題でそうとは言えないかもしれない。

⽂字情報基盤で⽤いている IVS は、現在、この IVD の中の“Hanyo-Denshi collection”という集合を⽤いているが、情報規格調査会は現在、⽂字情報基盤が整備した⽂字図形のうち未だ IVD に登録されていない⽂字図形を含めた⽂字図形全体を、改めて“Moji_Joho collection”として、新規登録する⼿続きを進めており(図 3 参照)、2014 年中にはその⼿続きが完了する⾒込みである。
(中略)
⽂字情報基盤が参照する IVD の collection は、2014 年度中に“Hanyo-Denshi collection”から“Moji_Joho collection”へ変更されることになるが、多くの IVS 値は新 collection へ継承されており、また、旧フォントを前提にして作成された⽂書ファイル等に対する、新フォントの上位互換性は保たれる。
文字情報基盤導入 ガイド | 一般社団法人 文字情報技術促進協議会

さて、CJK 統合漢字に収録されている「難しいナベ」のうち気軽に使えるのは U+9089 (邉) と U+908A (邊) の 2 つなので、例として U+9089 に対してどのぐらい異体字セレクタが当てられているのか IVD_Sequences.txt を確認してみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
9089 E0100; Adobe-Japan1; CID+6930
9089 E0101; Adobe-Japan1; CID+13407
9089 E0102; Adobe-Japan1; CID+14241
9089 E0103; Adobe-Japan1; CID+14242
9089 E0104; Adobe-Japan1; CID+14243
9089 E0105; Adobe-Japan1; CID+14244
9089 E0106; Adobe-Japan1; CID+14245
9089 E0107; Adobe-Japan1; CID+14246
9089 E0108; Adobe-Japan1; CID+14247
9089 E0109; Adobe-Japan1; CID+14248
9089 E010A; Adobe-Japan1; CID+14249
9089 E010B; Adobe-Japan1; CID+14250
9089 E010C; Adobe-Japan1; CID+14251
9089 E010D; Adobe-Japan1; CID+14252
9089 E010E; Adobe-Japan1; CID+20233
9089 E010F; Hanyo-Denshi; JA7821
9089 E010F; Moji_Joho; MJ026190
9089 E0110; Hanyo-Denshi; JTBD69
9089 E0110; Moji_Joho; MJ060248
9089 E0111; Hanyo-Denshi; JTBD38
9089 E0111; Moji_Joho; MJ060239
9089 E0112; Hanyo-Denshi; JTBD2D
9089 E0112; Moji_Joho; MJ060238
9089 E0113; Hanyo-Denshi; JTBD2C
9089 E0113; Moji_Joho; MJ060237
9089 E0114; Hanyo-Denshi; JTBD2A
9089 E0114; Moji_Joho; MJ060235
9089 E0115; Hanyo-Denshi; JTBD29
9089 E0115; Moji_Joho; MJ060234
9089 E0116; Hanyo-Denshi; JTBD27
9089 E0116; Moji_Joho; MJ058866
9089 E0117; Hanyo-Denshi; JTBD65
9089 E0117; Moji_Joho; MJ026197
9089 E0118; Hanyo-Denshi; JTBD2BS
9089 E0118; Moji_Joho; MJ060236
9089 E0119; Hanyo-Denshi; JTBD47
9089 E0119; Moji_Joho; MJ026191
9089 E011A; Hanyo-Denshi; FT2632
9089 E011A; Moji_Joho; MJ026194
9089 E011B; Hanyo-Denshi; JTBD49
9089 E011B; Moji_Joho; MJ026192
9089 E011C; Hanyo-Denshi; JTBD4CS
9089 E011C; Moji_Joho; MJ026195
9089 E011D; Hanyo-Denshi; JTBD64
9089 E011D; Moji_Joho; MJ026196
9089 E011E; Hanyo-Denshi; TK01090330
9089 E011F; Moji_Joho; MJ026193

いっぱいだ! 非常にうれしい! 前述の通り Moji_Joho と Hanyo-Denshi は親子のようなもので、同じ字形に対しては異体字セレクタを共有している。なのでこの行数分だけ字形があるというわけではないが、それでも!

自がハシゴでワ、ハ、口、点が 1 つのしんにょう

例えばこのナベも収録されているらしいので、適当に PHP で文字列連結して表示してみよう!

U+9089 から変わってないじゃん

え?

これはフォントとコレクションの関係性に注意していないと起こる事故である。

異体字シーケンスを使うときはフォントに気を付けようという話

その1: 対応フォントか要確認

誰でも使いやすいのは源ノ角ゴシック (Noto Sans) 系、ヒラギノ系、游ゴシックあたり。モリサワやイワタなどのメジャーどころは対応例が多いらしい。きちんと収録内容を確認して使おう。

adobe-fonts/source-han-sans: Source Han Sans | 思源黑体 | 思源黑體 | 思源黑體 香港 | 源ノ角ゴシック | 본고딕
Adobe-Japan1規格 - フォント専門サイト fontnavi

そして無料だが、フォントの性格上ガチ用途に限られる IPAmj明朝。

IPAmj明朝フォント | 一般社団法人 文字情報技術促進協議会

リリースノートを読んだ感じ、IPAex 系は SVS への対応のみらしい? IPAmj明朝は普段遣いするフォントではないので、IVS 目当てで使うなら「本当にその用途」というケースに限られそう。

IPAex リリースノート Ver.003.01 | 一般社団法人 文字情報技術促進協議会

ちなみに先ほどのスクリーンショットは源ノ角ゴシック。え?

その2: フォントによって対応コレクションが違う

源ノ角ゴシックと IPAmj明朝を例に取るなら、前者は Adobe-Japan1、後者は Moji_Joho (Hanyo-Denshi) に対応している。フォントの対応コレクションを使わないと意図した字形で表示されないのでこれまた注意が必要。

ところで上のスクリーンショットでは「Adobe-Japan1 にも収録されている字形を表示するのに、あえて Moji_Joho の異体字セレクタを使う」という意地悪をしている。そう、「自がハシゴでワ、ハ、口、点が 1 つのしんにょう」のナベは Adobe-Japan1 にも収録されていて、 U+9089 E0102 がそれにあたる。なのでざらっと全部表示してみると次のようになる。

比較してみる

そして「その1」で挙げた対応フォント例はほとんど Adobe-Japan1 のみの対応なので、IVD はまず Adobe-Japan1 を見るべきかなぁ……と感想を抱いたあたりで、この記事は終わり! Adobe-Japan1 にはバージョンがあるからそれにも気を付けよう!

結論

  • それ、CJK 互換漢字じゃない? 大丈夫? SVS 使う?
  • どうしても変換できない漢字を扱いたいときは異体字シーケンスの使用を検討しよう
  • IVS を使うときはフォントとコレクションの対応関係に気を付けよう
  • まだまだ異体字関係はめんどくさいな……。