Unicode入門のためのメモ集
Unicodeの基本
Unicode
人類の言語で使われる全ての文字を列挙し、それぞれの文字自身の一意な符号を与えるのを目的とした"仕様"
文字
文章の最小の構成要素。"A"とか"D"とか。
コードポイント(code point)
0から0x10FFFFまでの範囲の整数。文字に一意な符号が割り当てられる。
(110万個くらいあって現在は11万程度割当済)
-- 10進数
9822
-- 16進数
0x265e
-- Unicodeのドキュメントで使われる表記
U+265E
グリフ(glyph)
グラフィック要素の組のこと。文字はグリフで表示される。GUI側が選定してくれるので基本気にしなくておk。
エンコーディング
コードユニット
メモリ上でのエンコーディングの最小単位。
文字エンコーディング/エンコーディング
UTF-16 とサロゲートペア
固定長(16bit)の夢を諦めたUnicodeは21bit体制に。
それまでとの互換性を保ったまま可変長の文字表現を導入しないといけない。
しかしテキストデータの表現において処理するデータの単位 (これをコードユニットと言う) を 16bit から変えるわけにはいかない。(全ての処理系が21bit体制に追従は出来ないので。)
じゃあどうすんの???
コードポイントはなぜ0から0x10FFFF?
21bitは0x1FFFFF(2097151)、しかしコードポイントとして使えるのは0x10FFFF(1114111)...
=>TODO どゆこと?
0x400 という数はサロゲート領域と呼ばれるコードポイント空間のサイズ (の 2 分の 1)
スカラー値(Unicode scalar value)
文字を割り当てるコードポイント。通常のコードポイントと区別して、Unicodeではこう呼ぶ。
正確にはサロゲート領域に含まれないコードポイントのこと。
サロゲート領域(Surrogates:"代理"くらいの意味)
0xD800 から 0xDFFF のコードポイント空間を指し,この空間の値には文字は割り当てられない。
テキストデータへのエンコーディングを見据えてこういう空間が空いている(らしい)。
16bitと21bitの互換性
-
16bit 2つ分に分割して表現するという方法は?✖
ダメ
TODO デコードで詰まる話追加 -
文字を割り当てない領域(サロゲート)を利用 ◯
この方法の利点は主に下記2つ
(1)16bitを超えないコードポイントを持つ文字は影響を受けない
(2)それまでコードポイント1つを単純にコードユニットとして扱っていた実装は、コードユニット2つ分の文字は扱えないとすることでそれまでの処理は継続可能 =>TODO どゆこと?
デコードが一意になるようなエンコード/デコード
- UTF-16エンコーディング
基本的にはビックエンディアン(つまりhightの次にlow)
(1)コードポイントの一部を high surrogate、low surrogate という名の領域として確保し、そこは文字を割り当てないようにする
(2)0x10000 - 0x10FFFF のコードポイントは,0x10000[65536] を引き (つまり,0x00000 - 0xFFFFF[1048575] の 20bit 値になる),10bit の値を表すコードユニット 2 つとして扱う
(3)コードユニットの値は,上位 10bit は 0xD800 を足して high surrogate のコードポイントになるように,下位 10bit は 0xDC00 を足して low surrogate に収まるようにする
=> Unicode で格納できる文字が中途半端な上限を持っているのは,UTF-16 で表現できる値の上限で設定されているから!!
UTF-16 の互換性のため,Unicode のコードポイントには文字の割り当てが避けられてる領域があった。この領域はもちろん、Unicode scalar value ではないので、エンコードの対象ではない。
high surrogateとlow surrogate
high surrogate:0xD800(55296) - 0xDBFF(56319) (0b110110??????????)
low surrogate:0xDC00(56320) - 0xDFFF(57343) (0b110111??????????)
PEP383
文字列の内部表現を分ける2派閥
-
UTF-8 / UTF-16 / UTF-32 のいずれかを使っている言語
-
Unicode コードポイントの列を内部表現として使っている言語
実はPythonも
"使われない領域"の利用
文字表現として実質必要なのは 0 から 0xD7FF,0xE000 から 0x10FFFF の数値だけだ.なので,0xD800 から 0xDFFF の間は使われない領域ということになる。
Pythonではこの領域を「ASCII 互換の文字コードでエンコードされたバイト列に対し,デコードに失敗した文字を表現するため」に使われる。