エントリー

2010年06月04日の記事は以下のとおりです。

文字コード探訪

 一昔前,Shift-JISやJISなどの文字コードが主役だった頃は私もこのあたりの話にしっかりついて行けたのですが,最近のUnicodeの話は,どうも断片的に耳に入ることが多く,まとまった知識がなかなか得られずに気持ち悪い想いをしていました。

 そこで一発奮起,複数の資料を矛盾なく統合して,私なりの理解を得ようと試みました。以下はそのまとめです。実際,本当に正しいのかどうか怪しいところもありますので,もし指摘があればぜひ頂きたいと思います。

 最初に基礎として,ASCIIコードについてです。7ビットのコードで,アルファベットと数字を表現するために生まれました。0x00から0x1F,0x7Fはコントロールコードとなっており,実際の文字は0x20から0x7Eまでに割り当てられています。

 日本では,後に半角カタカナと呼ばれるカタカナを0xA1から0xDFまでに割り当て,8ビットのコードとして使う事が一般的に行われてきました。

 では,Unicodeの話を始めましょう。

 事の始まりは30年近く前,1980年代の初頭に遡ります。ISOは1983年,世界中の文字を1つのコードで扱う事を目指し,統一文字コードの検討を開始しました。

 当時ISOは,日本のJIS漢字コードと呼ばれた16ビットのコードが成功を収め,一般家庭にさえもコンピュータやワープロが普及していく状況を評価しており,これを全世界の文字で出来れば素晴らしいと考えていたようです。

 JISでは,エスケープシーケンスを使ってASCIIと漢字を切り替えます。

 ESC $ @ または ESC $ B のあとに続くコードを16ビット単位で処理して漢字を表すとし,ESC ( B または ESC ( J のあとに続くコードはASCIIとして8ビットで処理します。

 また,ASCIIで使われる範囲のうち,コントロールコードのある0x00から0x1Fのみを避け,16ビットの漢字コードを上位と下位の8ビットいずれにおいても0x21から0x7Eの範囲で表現します。

 7ビットあれば伝送可能という利点もあるのですが,処理が煩雑ですし,文字列の途中からではそれが漢字コードなのかASCIIなのか判別できないので,結構面倒臭いものでした。

 Shift-JISはこうした欠点をカバーするために考えられたもので,半角カタカナを含んだASCIIコードのうち,使用されていない0x81から0x9Fと,0xE0から0xFCを上位8ビットに置き,この部分が漢字コードであることを表します。続く下位の8ビットは0x40から0x7Eと,0x80から0xFCを使いますが,スペースなどの記号や0x7Fというコントロールコードを避けています。

 当然JISとはコードが違いますが,文字集合という観点ではJISと同じと言えるわけで,実は文字集合と文字エンコードがここで分離したと考えることが出来るかも知れません。

 ASCIIと半角カタカナと共存でき,かつ既存のシステムを大きく変更することなく日本語対応させることが可能という利点を持つShift-JISは,MS-DOSを始め,多くのパソコンに使われるようになり,一般化していきました。

 ついでに1980年代後半から90年代にかけてよく使われたEUCについても触れておきます。UNIXを日本語対応させるという目的で考え出されたコードで,半角カタカナを含まないASCIIコードのうち,上位8ビットと下位8ビットのいずれにも0xA1から0xFEを使って,16ビットの漢字を表現します。

 半角カタカナは0x8EA1から0x8EDFまでにマッピングされ,ASCIIでは8ビットで扱う事の出来た文字が,16ビット必要になる領域に飛ばされています。その代わり,上位8ビットを見ればそれが漢字コードであることが分かる(つまりASCIIとの重複がない)こと,エスケープシーケンスを使わずに済むこと,下位8ビットが完全にASCIIと重複しないことがあり,Shift-JISが持つ利点に加えて,Shift-JISよりも「美しい」ため,こだわりの多いUNIXの人たちの間で圧倒的な支持を受けていました。

 1980年代後半までに日本ではこうした動きがあったのですが,ISOとしては全世界統一文字コードとして16ビット固定長のコードを使い,コントロールコードを避ける形で用意した94x94の面を,日本語,韓国語,中国語の現在の文字コードの規格に1面ずつ割り当てて行くことを考えていたようです。

 でも,ちょっと考えると分かるのですが,すでにこの三カ国語の文字だけでもカツカツ,これ以外にそれぞれの拡張文字や台湾,香港の文字,もちろん他の国々の文字などを考えると,もう全然16ビットでは足りないことが分かってきます。

 そこでISOは16ビット固定長のコードをあきらめ,32ビット固定長で検討に入ります。これが後にUCS-4と呼ばれることになります。

 32ビットのうち先頭の1ビットは符号ですので,実質31ビットで扱う事の出来る文字数は実に21億文字以上となり,これだけあればまず困ることはないと思われますが,それまで8ビットですべてを表現出来ていた言語圏の人にとってはデータ量が4倍も増えることになり,ちょっともったいない所があります。

 こうしたこともあってか,ISOとは別にアメリカのコンピュータメーカーが中心となって統一された文字コードを作ろうという動きが出てきます。こうして誕生した団体がUnicodeコンソーシアムです。

 Unicodeコンソーシアムの検討には16ビット固定長という前提条件がありました。しかし,ISOがそうだったように,65000程度の文字数では,とてもすべての文字を割り当てられません。そこで日本語,韓国語,中国語で字源的には同じで,字形の異なる漢字に同じコードを与えるハンユニフィケーション(Han Unification)と呼ばれる方式を採用することになりました。

 この方法によって統合された漢字をCJK統合漢字といいます。かくして,Unicodeは16ビット固定長による,文字コードの統合に一応の決着を付けました。

 さて一方のISOは1991年,かねてから検討していた32ビット固定長の文字コードを使ったドラフトを投票にかけますが,すでに存在したUnicodeとの競合を避ける流れに飲まれ,否決されるに至ります。

 そこでISOは32ビットの文字コードのうち,先頭16ビットがすべてゼロの領域にUnicodeの16ビットコードを取り込むことで,Unicodeとの統一を図ります。

 1993年,この案は採択され,ISO/IEC10646-1として規格化されます。ここで規格化された32ビットコードはUCS-4と呼ばれ,このうち先頭16ビットがゼロの領域に割り当てられたUnicodeとほぼ同じ文字をBMP(基本多言語面)と呼ぶようになります。

 ところで,上位16ビットがすべてゼロであるBMPだけを扱うときは,ゼロが列んだ部分は省略したって構わないわけで,下位の16ビットだけを使うコードをUCS-2といいます。

 よく,UnicodeがISOを食ったとか,ISOを潰したとか,そういう物騒な話を耳にすることがありますが,それはこのあたりの話です。

 1993年のISO/IEC10646-1では,取り込んだUnicodeで使用している下位16ビットしか文字を割り当てることはしなかったのですが,2001年にはようやく上位の16ビットにも文字の割り当てを行うことになり,ISO/IEC10646-2として制定されます。しかし2003年には,両者を統合してISO/IEC10646をして再出発します。

 ここで,お約束です。Unicodeでは,文字の表現をU+という記号を頭に付けた「コードポイント」という数値で表現します。

 先程出てきた16ビットのUCS-2で「ABC」を書くと,はU+0041 U+0042 U+0043と表現されます。都合のいいことに,下位8ビットはASCIIと同じです。

 これが32ビットのUCS-4だと,「ABC」は,U+00000041 U+00000042 U+00000043と表現されます。お気づきと思いますが,上位16ビットは全部ゼロ,下位16ビットはUCS-2と同じです。これは,上位16ビットがゼロの時には下位16ビットはUCS-2と同じにすると言う,ISOがUnicodeを取り込んだという,まさにその結果です。

 さて,国際規格に自分達のコードを格上げできた勝者のUnicodeですが,同じ字源とはいえ,字形の違いがあるのはそれらを母国語とする人々にとっては面白くありません。

 そもそも,割り当て可能なコードが圧倒的に足りないことはUnicodeとしても問題で,結局Unicodeは「16ビット固定長」というポリシーを捨て,U+0000からU+FFFFまでに加えて,U+10000からU+10FFFFまでの約100万文字分を追加することを決定します。つまり,Unicodeは16ビットから21ビットに拡張されてしまった事になります。

 これをUnicodeの敗北という人もいますが,私はそうは思いません。事実,16ビットで決めた範囲で十分な場合も多いですし,そこで不足する分を足りないままで放置するのではなく,ちゃんと拡張するという方法で対応したことは,非常に誠実なことだと感じています。

 とはいえ現実的な問題として,すでにUnicodeは16ビットの範囲を使い切っていますので,増えた100万文字をなんとか表現する方法を考えねばなりません。そこで,これまでに割り当てのなかった上位サロゲートと呼ばれるU+D800からU+DBFFと,下位サロゲートと呼ばれるU+DC00からU+DFFFの2つを組み合わせを用い,拡張された部分を表現するときには32ビットを使うことにしました。(0xDBFF-0xD800)x(0xDFFF-0xDC00)=1024x1024=1048576で,ほら,一気に100万文字も増えたでしょう。

 上位と下位の2つのサロゲートで生まれた領域なので,これをサロゲートペアと呼びます。UTF-16と呼ばれるこのエンコード方式を導入して1996年に誕生したUnicode2.0は,これまでの65000文字に加えて,トータルで約110万文字を扱うことが出来るようになりました。ただし,このUTF-16が正式にRFCで制定されたのは随分遅くて,2000年です。

 UTF-16は,UCS-2を超えた範囲の表現に32ビットを使うという仕組みですので,UCS-2で表現出来るものは16ビットのままです。従って「ABC」はU+0041 U+0042 U+0043とUCS-2と同じになります。

 ここで誤解がないようにしたいのですが,後で出てくるUTF-8と今回のUTF-16は,同じUTFとなっていても,全く違う目的で作られた,全く異なるものだということです。UTF-16はあくまでサロゲートペアを使ってU+10000からU+10FFFFまでのコードをこれまでの16ビットのUnicode(つまりUCS-2)と混在させるために生まれたもので,UTF-8というのはASCIIの文字領域にUCS-2なりUCS-4を埋め込むものです。だから,UTF-16をUTF-8でエンコードすることも可能です。

 そんなこんなで,ISO/IEC10646はUCS-4という元々の4バイトコードで約21億文字,UCS-2というUnicode1.0そのものの2バイトコードで65000文字を規定することになり,一方のUnicodeは1.1までが2バイト固定長の65000文字,2.0でサロゲートペアとUTF-16の導入によって2バイトもしくは4バイトの可変長コードで約110万文字に拡張されることになったわけです。
 
 さらに話は進み,32ビット(実質31ビット)固定長でU+0000000000000000からU+7FFFFFFFFFFFFFFFまでの約21億文字分をカバーする最強のコードだったUCS-4は,2006年の改訂において,UnocodeがサポートするU+0000からU+10FFFFの110万文字のコード以外は永久に使用禁止,と決まるに至って,ISO/IEC10646とUnicodeはその基本的な能力を統一されることになりました。UCS-4は,とうとう牙を抜かれてしまったわけです。

 ところで,UCS-4もUCS-2(Unicode1.1)も,16ビットなり32ビットの空間をすべて使う事を前提としています。当然,これらを8ビットごとに区切った時に,ASCIIでコントロールコードに割り当てられている部分を避けるなどの配慮はされていませんから,UCS-2やUCS-4をそのままモデムなどの伝送路に流し込んでしまうと,偶然コントロールコードになってしまったある8ビットを誤認してしまうことになります。

 そこでコントロールコードを含むASCIIコードの部分は8ビットでそのままASCIIコードを示し,それ以外のコードを16ビットから48ビットの8ビット単位による可変長で表現するという文字エンコード方式が考え出されました。これがUTF-8です。

 こうすると,どの8ビットもコントロールコードを避けるように出来ます(もちろんわざとコントロールコードを流したい場合は別です)。言い換えると,UTF-8というのはUCS-2やUCS-4というバイナリコードを,テキストに変換する,なつかしのuuencodeとかbase64とかISHなどと同じ役割を果たしていることになりますね。

 ところで誤解のないようにしたいのですが,UCS-2にもUCS-4にも,コントロールコードは存在します。UCS-2におけるU+000DはCR(キャリッジリターン)ですし,U+000AはLF(ラインフィード)という,おなじみのコントロールコードです。

 エンコードというと符号化という日本語が割り当てられて,なんだか分かったような分からないような気分になるのですが,どうも英語で言うencodeには,ある数字を別の数字に置き換えることを広く示しているきらいがあり,正確な表現でないことを承知の上であえていえば,UTF-8などは「再マッピング」がもっともイメージの沸きやすい表現ではないかと思います。つまり,UCS-2やUCS-4を,ASCIIのコントロールコードを避けてテキストに割り当てられたコードに再マッピングを行うのが,UTF-8でやっている「エンコード」というわけです。

 余談ですが,Shift-JISという一昔前の漢字コードをこうした視点で見てみると,ASCIIに16ビットのバイナリである漢字コードを再マッピングしたという点ではUTF-8的であり,目的もUTF-8と同じだったりするわけですが,実際に行われた手法というのは空いているコードにASCIIからはみ出たコードを割り当てて8ビットと16ビットの可変長しているわけですから,極めてUTF-16的です。目標はUTF-8,やり方はUTF-16というのが,Shift-JISの正体というと,ちょっと言い過ぎでしょうか。

 UTF-8ではその昔(具体的には制定された1998年から2003年まで),UCS-2だけではなく,UCS-4もエンコード出来る事になっていたわけですが,2003年に新しいRFCによって規定されたUTF-8ではUTF-16で規定された範囲のみを扱う事になり,8ビットから32ビットの8ビット単位による可変長として再定義されました。これにより,UTF-8はUCS-4の範囲のすべてを扱う事が出来なくなってしまったわけですが,前述のようにUCS-4は2006年にUTF-16と同じ文字しか扱わないことになりましたので,これで実害はありません。

 UTF-8の面白いところは,8ビットのASCIIだけで間に合っている人たちにとっては8ビットだけでよく,16ビットで間に合っている日本語においては24ビットまでですべてを扱う事が出来る,という点でしょう。

 確かに16ビットで済んだところを24ビット必要だというのですからデータ量が増えることになりはしますが,16ビット固定長だったものが24ビット可変長になるわけですから,必ずしも1.5倍になるわけではないという点で,少なくとも日本語を扱うシステムにおいては,だたちに損になるという話ではなさそうです。

 それでいて,UTF-8を使えば100万文字を越える文字数を扱う事が出来るわけですから,これが本命として普及しているという話もなるほど納得というところでしょうか。

 最後にお詫びです。まず,UTF-16などの16ビットコードでは,エンディアンの問題がついて回ります。最初にリトルエンディアンもしくはビッグエンディアンと決めておく方法(UTF-16BE,UTF-16LE)もありますし,どちらのエンディアンを使っているかを示すマーク(BOMといいます)を頭につける方法もあります。しかし,これを言い出すとエンディアンとはなにか,から話をしなければならず,ややこしいので,本文では触れないことにしました。

 あと,異文体を扱う事の出来るVariation Selectorについても触れないことにしました。確かに重要なことではあるのですが,文字コードの体系を歴史的に整理するという目的から外れてしまうので,Unicode3.2に初めて登場し,Unicode5.1でようやく漢字の異字体に使われるようになったという比較的新しい仕組みについては,またの機会にしたいと思います。ちなみに,この仕組みを使うと,漢字一文字に64ビット必要になることがあるということだけ,雑学の1つとして覚えておいても良いかもしれません。

 もう1つ,コードの長さを言い表すのに,私は「ビット」を使いました。しかしこれは適当ではなく,本来なら8ビットを1まとめにした「オクテット」という単位を使うのがふさわしいです。

 ん?8ビットならバイトとちゃうの?と思うかも知れませんが,習慣でそうなっているだけの話であり,実は1バイトは必ずしも8ビットと決まっているわけではありません。ですから厳密に表現したい場合には必ず「8ビット」を示すオクテットという単位を用いることになっています。

 過去の話は別にして,現在は1バイトが8ビットとして浸透していますし,別にそれで問題はないのですが,文字コードや通信の世界では(うるさい人が多いこともあってか)オクテットを目にすることが多いのです。ですから,UCS-4は32ビットで間違いはありませんが,どちらかというと4オクテットという言い方の方が正しいわけです。

 間違っても4バイトと言ってはいけません。文字コード厨にこっぴどくやられてしまいます。

 さてさて,こんな感じで全世界の文字に数字を割り振ってコンピュータで使っていきましょうという壮大なお話をまとめてみましたが,1つだけ。

 実は現在すでにUCS-2という言い回しは存在しないことになっています。なぜなら,UCS-2はUCS-4に取り込まれていますし,そのUCS-4を表現するために生まれたUTF-16を使えば,UCS-2の範囲なら全く同一のコードとなりますので,単独でUCS-2が存在する必要性が全くなくなっているからです。

 さらに,前述のようにUCS-4はすでにUnicode2.0以降と統合されていますので,現実的に「Unicode」と言いさえすれば,もうややこしい過去の経緯や,ISO/IEC10646との関係,UCS-4やUTF-16といった細かい話を全く考慮しなくても,100万文字を越える世界中の文字を表現出来る文字コードを唯一示すことが出来るようになっています。

 だから,Unicodeを書き表すための数字のことをUTF-16ということを知っていれば,つまりUnicodeという言葉と,UTF-16という言葉を知っていれば,多言語の文字を扱うことに困りはしません。外と通信するときにUTF-8という「再マッピング」手段をしっていればなお良く,しかもUTF-8とUTF-16は全然別物と覚えておけば,もうそれで十分すぎると言うことができるでしょう。

 今後も,Unicodeという民間団体と,国際機関であるISOとの間で,食い違いが起きないように両者が歩み寄って,この仕組みを維持しようとしています。ひょっとすると同じ事をゴールに置く2つの団体と2つの仕組みが,デファクトを争っていたかも知れない過去を知ると,国際規格の本当の意味を見失わなかったISOという団体と,そしてISOに投票権を持つ国々の良心に,拍手を送りたくなるのです。

 現在Unicodeの(規格全体の)バージョンは5.2.0。昨年10月に制定されましたが,部分的には今年の5月にもアップデートが行われていますし,現在も活発な議論が行われています。人間らしさの源,文化の代表である文字をすべて網羅するという,人類の夢と理想に向かって歩き続けるこの活動こそ,世界平和に貢献するのではないかと,そんな気さえしてきました。

 付録として,軽く各バージョンをまとめておきます。

Unicode1.0 1991年10月 
16ビット固定長,日本語を含む約7600文字

Unicode1.0.1 1992年6月
CLK統合文字を採用。28000文字。

Unicode1.1 1993年6月
ISO/IEC10646に取り込まれる。約34000文字。

Unicode2.0 1996年7月
サロゲートペアを導入し21ビットに拡張。約39000文字。ハングルを大移動させたため1.xとの互換性がなくなる。

Unicode2.1 1998年5月
ユーロ記号を追加。約39000文字。

Unicode3.0 1999年9月
地名人名に使われる漢字が追加。約50000文字。かつてハングルのあった場所に多くの漢字が割り当てられる。

Unicode3.1 2001年3月
さらに漢字が追加され,BMP以外にも割り当てられる。約94000文字。

Unicode3.2 2002年3月
異字体セレクタが登場。約95000文字。

Unicode4.0 2003年4月
16しかなかった異字体セレクタが256に増加。約96000文字。

Unicode4.1 2005年3月
古代ペルシア語など言語を追加。約97000文字。

Unicode5.0 2006年7月
バリ文字,フェニキア文字,くさび形文字が追加。このあたりからどんどんマニアックに。約99000文字。ISO/IEC10646の改訂によりUCS-4がUnicodeと一致。

Unicode5.1 2008年4月
さらに多くの言語が追加された上,なんと麻雀パイまでサポート。異字体セレクタが漢字にも使われるようになる。約100000文字。

Unicode5.2 2009年10月
BMPに各国語が追加され,サロゲート領域にはヒエログリフなども追加。ついでにARIB外字も追加。約107000文字。

Unicode6.0 2010年9月予定
現在ドラフトの段階。砂時計,巻戻しや早送りのマーク,原子力マークやピースマーク,再生紙マーク,果てはドクロマークなどの絵文字が含まれる予定。約109000文字。

ページ移動

  • 前のページ
  • 次のページ
  • ページ
  • 1

ユーティリティ

2010年06月

- - 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 - - -

検索

エントリー検索フォーム
キーワード

ユーザー

新着画像

新着エントリー

過去ログ

Feed