unicode

Unicode, UTF-8, UTF-16

先日会社で PR 書くときに Unicode ってなんなんだっけ…? UTF-8 と UTF-16 ってなんだ…?となってその場はごまかしたんですが、さすがにぼんやりとでもわかっておいた方がいいのかなと思って調べてみました。
おそらく間違っていたりするので、訂正などいただければ…
書くにあたって色々読んだんですが、 Unicode および文字コードは魔境ということがわかりました。

Unicodeとは(はじめに)

符号化文字集合や文字符号化方式などを定めた、文字コードの業界規格
Unicode - https://ja.wikipedia.org/wiki/Unicode

つまりどういうことかと言うと、(1)符号化文字集合 と (2)文字符号化方式 などを定めた文字コードの業界規格ということらしいです。
符号化文字集合というのは、あ、い、う、え………などの文字の単なる集合のことを指していて、文字符号化方式というのは文字の集合の一要素に対してコンピュータ上でどう表すか、みたいな表現方法です。
(この Wikipedia の説明もなんだか誤解を招きやすくなっていると思うのですが…)
Unicode というのは単なる文字集合のことを表していて、文字符号化方式に UTF-8 / UTF-16 などがある、というイメージです。

Unicode の code point

code point というのは符号位置のことで、Unicode では、符号空間が 1 次元のため整数値がふられていて、特に Unicode スカラ値と言われます。U+ のあとに続けて 16 進数の Unicode スカラ値を書きます。
たとえば、以下のような対応付けがされています。ASCII 文字コードの符号位置は、そのまま Unicode における符号位置と同じになっていました。

1 => U+0031
A => U+0041
あ => U+3042

ここで重要なのは、この code point が符号化形式ではないということで、これを元に計算した結果が実際の文字コード(表現方法)になります。その計算方式に UTF-8, UTF-16 などがあります。
Unicode スカラ値の範囲については、 U+0000 - U+FFFF が BMP の範囲になっており、それ以降の U+10000 - U+10FFFF は BMP 以外を表しています。追加の歴史については Wikipedia がよくまとまっています。( https://ja.wikipedia.org/w/index.php?title=Unicode&action=edit&section=10#.E5.90.84.E3.83.90.E3.83.BC.E3.82.B8.E3.83.A7.E3.83.B3.E3.81.A8.E3.81.9D.E3.81.AE.E7.89.B9.E5.BE.B4
最大値が 10FFFF なのは、Unicode の符号空間が 21 ビットだからで、これ以降に文字を追加することはない、と(今のところ)言われています。
符号空間が 21 ビットなのは、国際標準である ISO/IEC 10646 に合わせており、この範囲内においては ISO/IEC 10646 と互換性があります。ISO/IEC 10646、最初は 31 ビットの文字空間があったんですが、色々あって 2006 年に正式に 21 ビット以降に文字を追加しないことになったらしいです。

UTF-8

8 ビット符号単位の文字符号化形式です。符号単位というのは、「その文字符号化形式において、意味を持つ最短のビット数」のことを言い、UTF-8 においては 8 ビット = 1 バイトが意味を持つ最短のビット数です。
表現方法としては、ASCII と同じ部分は互換性のためにそのまま 1 バイトとして表現します。
2, 3, 4, 5, 6 バイトについては、1 バイト目の先頭に 1 がどれだけ続くかで、何バイトで文字を表現しているのかわかります。また、その後の各バイトの先頭には 10 が付くので、バイトの区切りがわかりやすくなっています。
(8/2 追記)5, 6バイトについては不正としてはじくような定義がなされました。(https://tools.ietf.org/html/rfc3629#section-4)

2バイト : 11xxxxxx 10xxxxxx
3バイト : 111xxxxx 10xxxxxx 10xxxxxx
...

みたいな感じ。
そして、残りのビットに変換対象の Unicode code point を 2 進表記したものを右づめで入れていきます。(なお、ディレクトリトラバーサルなどの対策のため、ある文字を表現する最も短いバイト数以外は不正なものとして弾かないといけないです。)
細かい変換方法は https://ja.wikipedia.org/wiki/UTF-8 を読むとよいです。
読むとわかるんですが、変換対象の文字によってバイト数が変動するので、ファイルサイズには優しいですが、なんかデコードめんどくさそうに感じます。

UTF-16

16 ビット符号単位の文字符号化形式です。
Unicode code point が U+0000 - U+FFFF の範囲のものについては、1 文字 16 ビットのため、そのまま表現できます。(例: あ = (Unicode code point) U+3042 = (UTF-16) \u3042
それより大きい範囲については、サロゲートペアを使用します。
Unicode code point における U+D800 - U+DBFF(ハイサロゲート) と U+DC00 - U+DFFF(ローサロゲート) の 2 つを組み合わせて 1 文字を表現します。(イメージ的には二次元配列?)
これで、1024 * 1024 通りの文字を表現出来るようになります。
このため、Unicode code point において U+D800 - U+DBFF, U+DC00 - U+DFFF の範囲は定義されていません。( 2 つ組み合わせてはじめて文字が表現できるため)
悪名高いサロゲートペアも詳細を知るとなんだそんなもんかという気持ちになりました。(16 ビットじゃ漢字とか全部入れられないよね)
文字数カウントでバグるとかはただバイト数をカウントするんじゃなくて、範囲も見ないといけないのかな。

UTF-16 BE / LE

UTF-16 ビッグ / リトルエンディアンの略称です。詳細はバイトオーダなどで調べると出てくると思います。

UTF-32

32 ビット符号単位の文字符号化形式です。
32 ビット固定長のため、UTF-8, UTF-16 とくらべて文字によってサイズが可変しません。
ファイルサイズには優しくないですが、サイズが可変しないという点はとても良いと思います。が、どこで使うかはわかりません…
将来符号空間を 21 ビットから拡張する時にすごく役に立つとか…富豪プログラミングとか…?