本稿は "ファイル名における Unicode" と題してもよい内容になっていて、Unicode の様々な側面のうち、Mac OS X でファイル名を扱う場合に必要となる事柄にのみフォーカスをあてています。そのようにした理由は、あらゆる Mac OS X アプリケーションがサポートすべき領域であるからです。もし Unicode を扱うようなワードプロセッサを作っているような場合には、Unicode に関してここで扱うよりもたくさんの様々な理解が必要になることでしょう。本稿記載の情報の大部分は、Richard Gillam 氏の手になる素晴らしい書籍 "Unicode Demystified" に基づいています。しかし、Mac OS X でファイル名を適切に扱いたいというだけなら、800 ページもあるこの本のボリュームはちょっと多すぎるかも知れません。
訳注:"Unicode Demystified" Richard Gillam / Addison-Wesley / ISBN:0201700522 (Amazon)
Unicode はコンピュータによる利用/保持に適した形式で表記言語を表現するための、汎用テキストエンコーディング標準です。その目指すところは、全ての言語をエンコーディングできること、になります。全て、とは、少なくとも現在世界で用いられている表記形式の全てであり、同様に、すでに使われてなくなってはいるものの歴史的/学術的意義を持つ多くの言語をカバーすることを意味します。
Unicode 初学者には 2つの大きな学習ポイントがあります。1 つ目は用語の理解と扱いです。そして 2つ目は 1 つ目とも直接関わってきますが、表記言語と Unicode で文字がそれぞれどのように構成されているのかを知り、その 2つがどういった関係にあるのか(例えば、Unicode では文字がどのようにエンコードされているのか)を理解することです。英語はコンピュータで利用するために記述し、エンコードする上では、世界でもっとも単純な言語の一つです。ある意味でもっともな事ですが、英語を母国語としている人は、他の言語が Unicode でどのように記述されエンコーディングされるかについて、間違った仮定をしてしまいがちです。そして、プログラムのコードがそういった誤った仮定に基づいて書かれていると、全ての言語で正しく動作するものとはならなくなってしまいます。そこでまず最初に、Carbon-Development メーリングリストの Unicode に関する議論で、しばしば錯誤のもとになっている用語を定義してしまいます。
文字 (charactor) は、「ラテン文字の A」とか「'sun' という意味の中国文字」などのような言語上の抽象的概念の一つです。
Unicode で定義されているあらゆる文字は、単一の 21bit 抽象コードポイント値 (code point value)として割り当てられています(*1)。Apple は Unicode コードポイント値を Unicode スカラー値として参照しています。
MacTypes.h には次のようにあります。
typedef UInt32 UnicodeScalarValue; typedef UInt16 UniChar; typedef UInt16 UTF16Char; UniChar, UTF-16 のデフォルトの 16bit Unicode コード値です。UnicodeScalarValue の 0-0xFFFF は UTF16Char UTF-16 において、その値をもつ単一の UTF16Char として表現されます。UnicodeScalarValue の 0x10000-0x10FFFF は UTF16Char のペアとして表現され、片方は高位サロゲート範囲 (0xD8000-0xDBFF)、次に低位サロゲート範囲(0xDC00-0xDFFF) がきます。Unicode 3.0 以前の すべての文字は 0-0xFFFF の範囲に定義されており、「Unicode 文字」という用語は通常 UniChar = UTF16Char のことを指します。
Unicode 用語: 基本多言語面 (Basic Mulilingual Plane / BMP) はコードポイント値として U+0000 〜 U+FFFF の範囲を参照し、 これが元々の Unocode エンコーディング空間でした。その後、より多くの空間が必要だとわかり、新たに 16 の補足面 (supplementary plane)がエンコーディング空間として追加され、これによりコードポイント値は 16bit から 21bit に拡張されました。その結果、BMP は上位に 5bit 分の 0 bit を付加すれば適切な UniChar に変換できるコードポイント値の集合となったのです。n番目の追加面は U+n0000 〜 U+nFFFF の範囲のコードポイント値を含んでおり、n は 0x01 〜 0x10 の範囲(すなわち 16面)を取ります。よって、Unicode のコードポイント値の完全な範囲は 0x0000 〜 0x10FFFF ということになります。3面から 13面(U+30000 〜 U+EFFFF)は現在のところ将来のために予約されていて、まだ未使用の領域となっています。
サロゲート・ペア (Surrogate pairs) : この用語はよく Carbon-Development メーリングリストでも話題にのぼりますが、その定義を述べた人はいないので、ここに記します ;-) Unicode は BMP 中の 2,048 のコードポイント値 (U+8000 - U+DFFF) を実際の文字ではないものに割り当てています。これらは BMP の範囲外の文字を表現するときに使う、組み合わせペアの定義のために予約されているのです。そのような値をサロゲート (surrogate)と呼びます。最初の 1,024 個のサロゲート値 (U+D8000 - U+DBFF) は 高位サロゲート (high-surrogates)、残りの 1,024 個 (U+DC00 - U+DFFF) は 低位サロゲート (low-surrogate) と呼ばれます。サロゲートは高位と低位のペアで用いられた時のみ有効になるということを覚えておいてください。Unicode において、ペアになっていないサロゲートは、エラーと見なされます。
21bit のコードポイント値をどうやってサロゲートペアにマップするか頭を悩ませているのなら、次のようにします。最初に、元のコードポイント値から 0x10000 を引いて 20bit の値を得ます。そして 20bit を真ん中で分割して 10bit の値 2つと見なします。そして、先頭の 10bit が高位サロゲート値の下位 10bit となり、後ろの 10bit が低位サロゲート値の下位 10bit となるのです。おわかりでしょうか。
結合記号 (Combining marks) はそれ自体では文字を表現しないコードポイント値ですが、基となる文字を修飾する記号となります。読分け記号は combining mark の一つです。以下に例を示します。
é = e + ´ (U+0065 ラテン文字の小文字 e) + (U+0301 COMBINING ACUTE ACCENT)
書記素 (grapheme)はある種の表記言語における記述の最小単位です。特定の表記言語の読み手/書き手にとっては、通常、単一の「文字」として認識されます(*2)。
書記素クラスタ (grapheme cluster) は Unicode コードポイント (UniChar) 1つ、またはそれ以上の連続値となっていて、殆どの Unicode テキストにおいて、サーチ・ソート・ヒットテスト・方向キーの動作、等々の処理の最小単位として扱われます。Carbon-Developmetn メーリングリストや種々のドキュメント、また、ヘッダ中の kUCTextBreakClusterMask などにおいて、「クラスタ」といえば書記素クラスタのことを指します。
グリフ (glyph) は文字の具体的な可視的表現形式のことで、スクリーン上や印刷物で見ているもののことです。
- (*1)
- 正確にはちょっと違います。既存のテキストエンコーディングのいくつかと互換性を持たせるため、ごくわずかな文字に関しては Unicode でも 2つ以上のエンコードを持つものがあり、複数のコードポイント値を持ちます。しかしこれらの文字は相互に区別され、うち一つのみが文字表現として用いられることが必要です。このような場合にも、好ましいエンコーディングはあるにせよ、すべてを同一に扱うようにテキスト処理ルーチンを作るべきです。
- (*2)
- Richard Gillam 氏の "Unicode Demystified" を参照
Unicode はもともと全ての文字を一つの UniChar で表現することを目的としていましたが、それには無理があることが早々に明らかになりました。現在 95,000 以上の文字が Unicode 標準で定められており、すでに 16bit 値一つで表現可能な範囲をゆうに超えています。BMP に含まれるコードポイント値のみが UniChar 一つで表現可能なのです。かてて加えて、基となる文字に 1つ以上の読み分け記号やその他の combining mark を足して表現する文字が数多くあります。概念上の文字と、それを表現する Unicode 文字との間に 1対1 の関係があると仮定してしまうことは、Unicode 文字列を扱う上でもっともよく陥る失敗の 1つです。文字列の切りつめ処理を、不適切なオフセットで行ってしまうことになるわけです。Unicode 文字列の切りつめ処理や、挿入/削除には、常に適切な API 群を使う必要があります。("FSRefs と Unicode ロングファイル名" の「長さによって切りつめる」を参照)
(これに関するサンプルをどこかから見つける必要はありません。Mac OS X では Unicode へのエンコードは File Manager が処理しますが、"résumé" という文字列は UniChar 8個を含んでいて、最後の一つを削除すると "résume" になるのです。)
Unicode 文字列を連結することができます。個々の文字列はそれぞれ意味を持ち続けます。例えば、既存の文字列にその意味を変えることなく、".txt" という Unicode 文字列を追加することができるのです。さらには、英語とアラビア語(右から左へ記述する言語です)を結合して期待通りの結果文字列を得ることだってできるのです。
32bit エンコーディングならば、コードポイント値とエンコードされた値を 1対1 で直接対応づけられるし、安全に挿入や切りつめ処理を行いたい場合の問題も取り除くことができます。21bit はおよそ 100万文字を表現可能で、現在エンコードに使われている数のざっと 10倍です。しかし、コンピュータが処理しやすい大きさのデータ型で 21bit を表現可能な最小値は 32bit です。32bit データ型によるエンコーディングだと、無駄な領域が非常に多くなるのです。もし Unicode が全てのコードポイント値を単一の値でコード可能な 32bit エンコーディングを採用したならば、各文字について少なくとも 11bit が無駄になります。そして通常の利用方法の大部分だと、実に 1文字あたり 16bit の無駄が生じます。例えば HFSUniStr255 (Mac OS X でファイル名の表現に使われています)が 32bit ベースになったら、多くのファイル名には BMP 中の 40〜50文字以下しか使われていないにも関わらず、全体で 1022バイトになってしまいます。
(C) 2003 SkyTag Software, Inc.
訳文に対してのフィードバックは amn@mugiwara.jp まで