Unicode正規化 その5(最終回) (2010.3.26)

結合文字列を合成するために NFC を使うのは危険です。

どうしてこんな仕様になっているのでしょうか?
そして、結合文字列を安全に合成するにはどうしたら良いのでしょうか?

Unicode正規化の目的

NFCがどうしてこんな仕様なのか、答えを最初に言っておきましょう。Unicode 正規化は、実は一般ユーザ用ではなく、エンジニア用の規格なのです。

「Unicode照合アルゴリズム」という規格があります。これは文字をソートする時にその順序を求める仕組みです。
UNICODE COLLATION ALGORITHM

Mac OS X ユーザは日常的にUnicode照合アルゴリズムの恩恵を受けています。
Mac OS X: Finder が名前でソートされた項目を並べる方法

照合アルゴリズムは、通常それが働いていることを一般ユーザが知ることはありません。一般ユーザはその結果だけを得られればいいので、いちいち「照合アルゴリズムでソートしています」なんて知らされても鬱陶しいだけです。エンジニアもなるべくユーザに分らないように配慮します。つまり「裏方で働く規格」なんですね。

Unicode正規化は、この照合アルゴリズムで使われます。要するに「裏方の裏方で働く規格」です。そこでは「神」の旧字体を新字体に変換して、「神」の旧字体と新字体を同一に扱っても問題ありません。文字の順序を求めるための「一時的な使い捨て文字」だからです。ユーザが直接扱うフロントエンドの文字を変えることはしません。(ソートしたら字形が変わった、なんてありえませんよね)

結合文字列の問題は、1998年にHFS+がMac OS 8.1に導入されたのが潜在的な端緒だと言っていいでしょう。HFS+はアイテム名をすべてNFD(正確には改変したNFD)に分解して保持します。しかし当時はフロントエンドで Unicode から各国のレガシーエンコーディングに変換されていましたから、結合文字列をユーザが扱うことはありませんでした。この時点では「裏方」だったわけです。

それが OS X で突如として HFS+ の分解されたアイテム名がそのままフロントエンドに現れるようになりました。裏方がそのまま舞台の表に登場してしまったのです。問題が顕在化したのはここからです。

OS X ユーザが結合文字列を日常的に触っている現状は、実は異常なことです。

安全に合成するにはどうしたら良いのか?

しかし、Apple も Unicode Consortium もこれを深刻には考えていないフシがあります。 というのは、安全に結合文字列を合成する規格がいまだに存在しないからです。かわりにどの文書でもNFCに正規化することしか書いてありません。これは異常なことです。

Mac版のAdobeアプリケーションは「クリップボードのUnicodeテキストを一律にNFCに正規化してからペーストする」のが標準仕様になっています。InDesign でも Illustrator でも「神」の旧字体をペーストすると新字体に変わってしまいます。これも異常なことです。

Macだけの話ではありません。むしろ Windows の方が危険です。レガシーエンコーディングの CP932 に含まれる IBM拡張文字の漢字を見てみましょう。CP932からUnicodeへ無事に変換できたとしても、そこにNFCを適用すると一部の漢字が別の文字に変わってしまいます。

IBM拡張文字の漢字は「人の名前に必要な漢字」として外字領域に収められた経緯があります。人の名前の文字が気がつかないうちに勝手に別の文字になってしまうのは相当にヤバいのです。(Mac版のAdobeソフトは常にこの危険があります。文字原稿はたいていWindowsで入力されますので)

さて、規格がないのですから、この異常事態をなんとかするために、私は自作のソフトに「安全に結合文字列を合成する機能」を勝手に作って実装してしまいました。おそらく現時点(2010年3月)では、これができるのは私のソフトしかないと思います。

なにをどうしているのかというと、NFCの問題は合成除外文字を分解してしまうことにありますから、合成除外文字以外にNFCを適用するようにしています。合成除外文字を正規化の対象外にすることで、意図せず文字が変わることを回避しています。

以下はそれを実装した箇所です。(こう書くとなにか宣伝みたいですが、紹介したところで私には一銭の利益にもならないんですよ!)

Change into Plain Text

クリップボードのテキストをプレーンテキストにするソフトです。環境設定の「結合文字列をできるだけ1文字に合成する」を ON にすると、プレーンテキストにすると同時に「安全な合成」をします。デフォルトは OFF になっているので、ON にすることをオススメします。

浮紙

エディタです。テキスト処理の [Unicode] > [結合文字列を合成] で「安全な合成」をします。

FILL InDesign

InDesign用のソフトです。これの「完全フォーマットなしペースト」で InDesign に正規化させることなくペーストできますが、同時に「安全な合成」もしています。

他のソフトにもちょこちょこ使っていますが、ユーザの方には直接関係がない裏方なので割愛します。

終わりに

「その1〜5」で私が言いたかったのは Unicode正規化はユーザが直接扱うフロントエンドのテキスト整形用ではない ということです。もし「ソートすると一部の文字が変わる可能性がある」としたら、あなたはそのソート機能を使いますか? 怖くて使えませんよね。NFCもそうです。Unicode Consortium はどうやら「フロントエンドのテキスト整形用にも使える」と考えているようですが、ユーザにしてみれば怖くて使えないものなのです。

でも、合成除外文字を正規化の対象外にすれば問題を回避できます。これには異論があるかもしれませんが、現状では最も現実的な解だと思います。

私のスキルは日曜大工レベルなので、実は恥ずかしいほどベタな方法でこれをやっています(恥ずかしすぎて公開できません… 赤っ恥覚悟で公開しました)。これをシェルコマンドで実行できるようにすると、泣いて喜ぶ人がたくさんいると思います。私にはできないので、誰か作ってください!

追記:hirakunさんのPerlスクリプトでシェルコマンド風の処理もできるようになりました。

ご注意

この「Unicode正規化 その1〜5」のドキュメントは、正確さよりも分かりやすさを目指したものです。

文字コードがらみの情報を分かりやすく伝えようとすると、どこかしら正確ではない箇所が出てきます。しかし正確さを確保しようとすると、ごく一部の人にしか理解できない難解なものになってしまいます。

Unicode正規化はとりわけ混乱しやすい規格です。そのうえ日本では訳語まで混乱しているので、まずは「読んで混乱しないこと」に最も重点を置きました。それに成功したかどうかはちょっと微妙なところですが、正確ではない記述があることはお含みおきください。

もし「ここはあまりに不正確すぎる」という箇所がありましたら、ご指摘いただけると助かります。