Maxyは、Unicode対応のテキストエディタです。 ところで、そもそもUnicodeって何なのでしょうか。 UnicodeとUTF-8とかUTF-16との関係は? そのあたりのことを簡単に説明していきます。
まずは基本中の基本だけど、(現在のところ)コンピュータが扱えるのは数値のみという大前提がある。 何をするにも数値をいじるしかない。 きれいなCGも、優雅な音楽も、つまるところ数値なわけだ。 「ここに100を書き込んで、ここに150を書き込んで…」と、コンピュータが必死になっていろんなところに数値を書き込んだ結果としてCGが表示されたり音楽が流れたりするわけ。
そんなわけだから、当然コンピュータ内で文字を表現するにも数値が必要で、あらかじめ「この文字はこの数値で表す」という取り決めをする必要があるんだ。 その取り決め(文字⇔数値の対応)のことを文字コード、1つ1つの文字に割り当てられた数値のことをコードポイントと呼んでいる。 英数字とか記号とかの基本的な文字を集めた文字コードがASCIIと呼ばれるもので、現在使われてる文字コードのほとんどはこのASCIIを基に拡張されている。
ASCIIは、7bit(27=128通り)で文字を表現している(昔は8ビット目、つまり最上位ビットは誤り検出用のパリティビットだったらしい。今は0だけどね)。
例えば、文字"A"のコードポイントは41H
、といった具合。
アルファベットは大文字・小文字がそれぞれ26文字ずつ。
さらに数字が0~9までの10通り。
それにいろいろな記号を加えても128通りあれば十分にお釣りがくるってわけ。
コンピュータはアメリカを中心に発達してきたから、当面はそれだけで十分だった。
英数字しか使わないアメリカ人にとっては「文字コード=ASCII」という認識で何も困らない。
月日は流れ、日本でもコンピュータが発達してきた。 ただし、日本語はASCIIのように都合よくいかない。 ひらがなだけでも50文字。 カタカナを含めるとその倍の100文字。 これにさっきの英数字を加えるだけで、7bitなんて軽くオーバーしてしまう。 さらに、日本語には「漢字」なんていうものまであるから、どう逆立ちしても従来のASCIIではまかない切れない。 そこで、「英数字は1byteで、日本語は2byteで表そう!」ということを誰かが考えた。 つまり、こういうこと。
00H
~7FH
)は、そのままASCII文字として扱う80H
~FFH
)が現れたら、次の1byteと組み合わせて2byteの数値を作り、その数値に文字を割り当てるこれはなかなかうまい考え。 何がうまいかって、この方法だと従来のASCIIで書かれた文書も読めるし、日本語と英数字が混じった文書も矛盾なく読めるから。
ただし、ここで問題が発生。
コンピュータはパソコンやワークステーションなど、いろいろな方面から発達してきたから、日本語の文字コードもパソコンとかワークステーションで独自に発展してきちゃったわけ。
例えば、パソコンで一般に使われる日本語の文字コードはShift-JIS、ワークステーションはEUC-JPといった具合。
別々に発展してきたから、同じ文字でも「あ」のコードポイントはShift-JISでは82A0H
だけど、EUC-JPではA4A2H
といった具合に別々のコードポイントが割り当てられちゃったのだ。
ということは、逆に考えれば2つの文字コードの全く別の文字のコードポイントがたまたま同じ値を指し示すこともあるってこと。
例えば、EUC-JPで「表示」という文字は[C9 BD BC A8]
というデータ列で表されるけど、これを間違ってShift-JISで読み直したら「ノスシィ」とかいうわけのわからない文字になってしまうわけ。
どこのSNSだよ。
それとも結婚情報誌か?
っていうか、Maxyも似たような響きじゃねーか。
ちくしょう。
とにかく、これが文字化けと呼ばれる現象。 要するに、文字コードの判定ミスからおきる問題ってことです。 実際は他の要因で起こることもあるけど、ほとんどは判定ミスが原因。
余談だけど、今ではmojibakeは英単語として通用するらしい。 それもそのはず、ASCIIだけで十分なアメリカでは2種類以上の文字コードを使う必要はなく、当然文字化けなんて問題は起こらない。 そもそも文字化けなんていう概念すらなかった。 日本でコンピュータが発達してきて初めて直面した問題なわけだ。
さらに、コンピュータは日本だけじゃなくて韓国とか中国とかでも使われるわけで、当然それぞれの国に独自の文字コードがある。 だけど、自分の国の文字(と英数字)以外のことは何も考えてないもんだから、
「ありがとう」を中国では「谢谢」、韓国では「고마워요」と言います。
みたいな、いろんな国の文字が混ざった文書は書けなかったりする。 これじゃ国際化も何もあったもんじゃない。
こんな感じでいい具合にカオスになってきた文字コード。 そんな時、ゼロックスが「言語ごとに文字コードがあるのはややこしい。世界中の文字をすべて含んだ新しい文字コードを作ろう!これからはみんなでその文字コードを使えば1つの文書内で多言語を混在できるし文字化けの心配もしなくて済むしみんな幸せだ!」と言い始めた。 これがUnicode(単一の文字コードという意味)の始まり。 そして、ユニコード・コンソーシアムとかいう組織が作られた。 ユニコード・コンソーシアムには、マイクロソフト・アップル・IBM・サン・マイクロシステムズ・ヒューレット・パッカードといった錚々たる大企業が参加してるそうな。 日本からはジャストシステムも参加してるらしい。
最初は「これからは、全ての文字を16bit(216=65536通り)で表そう!それなら世界中の文字を含めることができる!」と張り切ってた。
そして「Unicodeを使う時は、コードポイントはU+12AB
のように16進数の頭に"U+"をつけて表そう!」という決まりを作った。
そんなこんなで1991年に当面必要な文字だけを収録した16ビットの文字コードとしてUnicode 1.0が公表されて、Unicodeは順風満帆かと思われた。
でも、残念ながら世の中そんなに甘くない。 日本人ならその理由はなんとなくわかると思う。 日本で普通に使われる常用漢字だけで2000字近くあるし、漢字検定1級で出題される可能性がある漢字は6000文字らしい。 ちなみに、漢字の本場・中国の漢字字典「康熙字典」では50000字近い漢字が収録されている。 一生かかっても憶えられる自信なんてありません。 漢字だけでこんな状態だから、タイとかベトナムとかシンガポールとか古代文字(何しろ「世界中の全ての文字」を表そうとしてるんだから…)なんかを入れると当然16ビットなんかじゃ全然足りないわけで。 Unicode 1.0の公表後に、いろんな国から「この文字も追加しろ!」と言われちゃって、「世界中の文字を16bitで表す」という野望は露と消えてしまいましたとさ。 諸外国(特にアジア)がこんなにたくさんの文字を使っているとは、欧米人は夢にも思わなかったみたい。
「16bitで足りないなら17bitとか18bit使えばいいやん!」と言いたいところだけど、残念ながら17bitとか18bitとかはコンピュータで扱うのが面倒だったりする。 もともとUnicode(というか文字コード自体)はコンピュータで文字を表現するためのものだから、コンピュータで扱いにくい文字コードにしたらそれこそ本末転倒。
コンピュータで扱いやすいサイズといえば、やっぱり8bit単位。 もっと細かく言うと「8x2nbit単位」が扱いやすい。 だから8bit(8x20)とか16bit(8x21)とかは無問題なわけ。 で、16bitが使えないとなると、次は32bit(8x22)になる。 これだと40億通り以上(232=4294967296)の文字を表せるから十分世界中の文字を収録できる…んだけど、何しろ無駄が多すぎる。 当面必要な文字は16bit内に収まっていて、これ以外の文字が必要になることはほとんどないのに常に1文字につき32bit使っていたら無駄無駄無駄ァァ!! 16bitでは足りなくて32bitでは多すぎる。 絵に描いたような「帯に短し襷に長し」状態。
実は、Unicode 1.0でも65536通りの全てに文字を割り当ててたわけじゃなくて、ところどころ空きがあったりする。
その中に、D800H
~DFFFH
という2048文字分の空き領域がある。
そして「この範囲の文字が出てきたらちょっと特殊なことをしよう」と考えた。
どう特殊かというと、「前半1024文字(D800H
~DBFFH
)のどれかが出てきたら、その次には必ず後半1024文字(DC00H
~DFFFH
)のどれかを出すようにしよう!これで表せる文字が100万文字(1024x1024=1048576)くらい増えた!」というわけ。
で、この「2つで1文字を表す」ペアのことをサロゲートペアと呼ぶことにしましたとさ。
つまり、文字データの中にD800H
とかD801H
とかが出てきたら、その次は「DC00H
~DFFFH
のどれか」が必ず出てくる、というルールを決めておく。
そうすれば、「D800H
に対して1024通り(DC00H
~DFFFH
のどれか)」、「D801H
に対して1024通り」…「DBFFH
に対して1024通り」の表しかたがあるので、結局1024x1024=1048576通り増えるということ。
何のことはない、Shift-JISとかの日本語文字コードで普通にやってたのと似たような方法じゃん。
これでUnicodeで表せる文字範囲はU+0000
~U+10FFFF
まで広がった。
16ビットで表せる範囲内の文字(U+0000
~U+FFFF
)は「基本多言語面(BMP)」、残りの100万文字余り(U+010000
~U+10FFFF
)は「追加多言語面(SMP)」として区別することにしたそうな。
当初の構想では16bitで全ての文字を表そうとしていたUnicodeだけど、仕様がここまで複雑になってしまったので効率的なエンコード、つまり8bitのデータ列で表す方法を考えなきゃいけなくなった。 つまり、16bitだけで全てが表せるなら何も考えずに全ての文字を16bitで表せばよくて、あとはエンディアンの問題だけを考えればよかったんだけど、サロゲートペアなんでものが出てきて中途半端に32bitも使うことになってしまったわけだから、なんかうまい方法を考えなきゃ、というわけ。
そこで出てきたのがUTFというエンコード方式。 ただ、このUTFもいくつか種類があって、それがまた混乱に拍車をかけてたりする。
実際には他にもUTF-7とかUTF-EBCDICとか、さらにはUTF以外のエンコード方式もあるけど、あんまり気にしなくていい。 ていうかよくわかんない。
余談だけど、「BMP内の文字だけを16bit固定で表した文字コード」をUCS-2と呼ぶ。 つまり「UTF-16のサロゲートペアがないやつ」。 BMP外の文字はよほどのこと(研究目的とか)でなければまず使われることはないので、普通に使う分にはこれで十分なのだ。 ちなみにMaxyも、内部コードは事実上UCS-2だったりする。 APIの仕様上、BMP外の文字の表示・編集もできるけど、カレットが変なところに来ることがあるよ。
ここまでくると、「UnicodeとUTF-16は同じなんだよね?UTF-8とかUTF-32はUnicodeとは違うの??」と混乱する人がいるかもしれない。 そこで、もう一度おさらい。
Unicodeとは、ASCIIやShift-JISのように、世界中のあらゆる文字を単一の文字コードで表そうとして策定された文字コード自体を指す。
で、UnicodeはU+0000~U+10FFFFの21bitで表されるけど、コンピュータでデータを扱うときは8bit単位が一番扱いやすい。 コードポイントを8bitのデータ列にエンコードするための方式がUTFで、具体的にはUTF-8、UTF-16、UTF-32の3つがあるということです。
要するに、Unicodeというのは世界中の文字を数値に置き換えた際の数値を指し、UTFというのはそれを8bitのデータ列にエンコードする方式のことを指していると考えればいい。
当然同じ文字でもエンコード方式が違えば異なるデータ列になる。
例えば、「𠀋(「大丈夫」の「丈」とは違うよ)」という文字を考えてみよう。
これはBMP外の文字で、コードポイントはU+2000B
。
そしてこれをUTF-32のデータ列で表すと[0002000B]
、UTF-16では[D840 DC0B]
、UTF-8では[F0 A0 80 8B]
となる。
表現方法は違うけど、どれも同じU+2000B
を指してるってこと。
結局、1つのコードポイントをデータ列で表現するのにUTF-8とかUTF-16とかUTF-32とかいろいろな方法ができて、ややこしくなっちゃった。 だって、コードポイントが共通でも、バイト列が何種類もあったらやっぱり誤認識・文字化けの問題は出てくるからね。
そこで、「どの表し方を使っているのかという情報」をデータの最初に置くことを誰かが考えた。
つまり、「これから始まるデータ列はUTF-8だ!」といった情報をデータの最初につけておけば、正しく読み取れる。
だから文字化け問題も解消する!というわけ。
そのデータがBOM。
その実態は、コードポイントU+FEFF
をそれぞれの方式でエンコードしたデータ列。
だから、文書の先頭にこのデータ列が出てきたら「これはBOMなんだな」と判断できるわけ。
本来は「バイトオーダーマーク」という名の通り、データのバイトオーダー、つまりビッグエンディアンかリトルエンディアンかを識別するためのマークだったんだけど、結果的にこのBOMによってエンコード方式までわかるようになったから、エンコード方式の判別にも使われてるとゆーこと。
UTF-16なら[FE FF]
、UTF-32なら[00 00 FE FF]
、UTF-8なら[EF BB BF]
というデータ列になる。
正確に言うなら、ビッグエンディアンのUTF-16が[FE FF]
、リトルエンディアンのUTF-16が[FF FE]
、ビッグエンディアンのUTF-32が[00 00 FE FF]
、リトルエンディアンのUTF-32が[FF FE 00 00]
って感じ。
UTF-8はそもそもバイトオーダーに依存しない仕様だから本来の意味(バイトオーダーを示す)でのBOMじゃなく、エンコード方式を表明するためのBOMなんだけどね。
あー、ややこしくなってきた。 まとめておこう。
エンコード方式 | BOM | |
---|---|---|
UTF-8 | EF BB BF |
|
UTF-16 | ビッグエンディアン | FE FF |
リトルエンディアン | FF FE |
|
UTF-32 | ビッグエンディアン | 00 00 FE FF |
リトルエンディアン | FF FE 00 00 |
うん、表にするとスッキリ。
要するに、先頭の4byteを読み込んだ時点で文字コードとバイトオーダーがわかるというスグレモノなわけだ。
U+0000
は普通の文書には現れないコードポイントだから、リトルエンディアンのUTF-16とUTF-32を間違える心配はない(リトルエンディアンのUTF-16に[FF FE 00 00]
とゆーデータ列がくることはない)。
逆に、先頭2byteだけだと両者を区別できないから、きちんと4byte読んで判断してね、ということ。
BOMは必ずしもつける必要はないんだけど、その場合はビッグエンディアンにするように決められてる。 ただ、BOMをつけないと認識ミス(≒文字化け)が起こる可能性があるからつけたほうが無難かな。
そんなこんなで、Unicodeのバージョンは今や5.1.0(2008年6月現在)にまで上がっている。 一時はハングル文字のコードがごそっと別のところに割り当てられたり(ハングルの大移動)、それなりの混乱もあったみたい。 仕様もどんどん複雑化していて、合成文字とか双方向文字とか、もうお腹いっぱい。
がんばれ、Unicode。 君はこれからどこへ行く?