文字コードの話 (3) - Unicode

文字コードについて調べたことをまとめます。

関連記事

目次

Unicode

Unicodeは各国・各文化の文字コードの互換性を解決することを目的に、世界の全ての文字を一つのコードページに統一するという考えで作られた規格。ISOには「ISO/IEC 10646」として制定されている。
1984年から策定され始めたISO 10646と、1987年からXeroxらによって検討され始めたUnicodeを、1991年に統合して制定された。

符号位置の数は0x110000 = 1114112個。0x10000個ずつを1面(plane)とした、全17面で構成される。

第0面
U+0000-FFFF
基本多言語面
第1面
U+10000-1FFFF
追加多言語面
第2面
U+20000-2FFFF
追加漢字面
第3面
U+30000-3FFFF
第三漢字面
第4面
U+40000-4FFFF
未割り当て
第5面
U+50000-5FFFF
未割り当て
第6面
U+60000-6FFFF
未割り当て
第7面
U+70000-7FFFF
未割り当て
第8面
U+80000-8FFFF
未割り当て
第9面
U+90000-9FFFF
未割り当て
第10面
U+A0000-AFFFF
未割り当て
第11面
U+B0000-BFFFF
未割り当て
第12面
U+C0000-CFFFF
未割り当て
第13面
U+D0000-DFFFF
未割り当て
第14面
U+E0000-EFFFF
追加特殊用途面
第15面
U+F0000-FFFFF
私用面
第16面
U+100000-10FFFF
私用面
  • U+0000-U+00FFの範囲(Basic Latin)はIEC_8859-1を拡張するように定義されている。(つまりASCIIの拡張となっている)
  • その後は各国・各文化の文字が続き、U+3041でひらがなが、U+4E00で漢字が登場する。
  • U+D800-U+DFFFにはサロゲートペアという後述のUTF-16の仕組みで使うための符号が配置されている。

特殊な例も含めて、Unicodeの文字の例を以下に挙げる。

文字 符号位置 説明
A U+41 ラテン文字の「A」
U+3042 ひらがなの「あ」
U+4E00 漢数字の「一」
U+32FF 「令和」を表す1文字 (2019/05/07追加)
U+5B89 漢字の「安」
U+FDFD コーランの一節「慈悲あまねく慈愛深きアッラーの御名において。」を表す1文字
𝔅 U+1D505 フラクトゥールの「B」
🀀 U+1F000 麻雀牌の「東」
𩸽 U+29E3D 魚の「ホッケ」を表す漢字。
日常で使う可能性がある範囲で、比較的後ろ側に収録された漢字の一例。
この文字をUTF-8でエンコードすると4バイトになる。(F0 A9 B8 BD)
𰻞 U+30EDE 画数の多い漢字として有名なビャンビャン麺の「ビャン」。
2020年に追加されたが、残念ながら現在はこの字を表示できる環境は少ない。
漢字構成記述文字で「⿺辶⿳穴⿲月⿱⿲幺言幺⿲長馬長刂心」とも表される。

異体字セレクタ

「葛飾区」の「」と、「葛󠄀城市」の「葛󠄀」は異なる形の文字であるが、Unicodeでは同じU+845B(葛)に収録されている。このような微妙な違いのある文字の表現のために、Unicodeでは異体字セレクタという仕組みが用意されている。
この場合は、U+845B U+E0100と2文字続けて書くことで「葛󠄀」という文字を表現することができるようになる。(U+FE0Eが異体字セレクタ)

異体字セレクタには以下のような種類がある。

符号位置 説明
U+180B-U+180D モンゴル自由字形選択子(FVS)
U+FE00-U+FE0F SVS(標準化された異体字シーケンス)の異体字セレクタ
U+E0100-U+E01EF IVS(漢字異体字シーケンス)の異体字セレクタ

異体字セレクタの使用例を以下に挙げる。

種別 文字 符号位置
U+1800 U+180B
FVS ᠀᠋ U+1800 U+180B
FVS ᠀᠌ U+1800 U+180C
FVS ᠀᠍ U+1800 U+180D
FVS ᠀‍ U+1800 U+200D
U+793E
SVS 社︀ U+793E U+FE00
IVS 社󠄁 U+793E U+E0101
U+2728
SVS ✨︎ U+2728 U+FE0E
SVS ✨️ U+2728 U+FE0F
U+845B
IVS 葛󠄀 U+845B U+E0100
IVS 葛󠄁 U+845B U+E0101
U+8FBB
IVS 辻󠄀 U+8FBB U+E0100
IVS 辻󠄁 U+8FBB U+E0101

Zero Width Joiner

Zero Width Joiner(ゼロ幅接合子, ZWJ)は2つの独立した文字を接合する形で表示するために用いる文字。
符号位置はU+200D。HTMLでは‍と記述することもできる。
元々アラビアやインドなどの文字のために用意されたものだが、現在は別記事で解説するEmoji(絵文字)にも多用される。

以下に使用例を挙げる。

文字 符号位置 備考
څ U+0685 ペルシア文字
‍څ U+200D U+0685
څ‍ U+0685 U+200D
क्ष U+0685 U+094D U+0937 デーヴァナーガリー
क्‍ष U+0685 U+094D U+200D U+0937
👨👩👦 U+1F468 U+1F469 U+1F466 Emoji
👨‍👩‍👦 U+1F468 U+200D U+1F469 U+200D U+1F466

逆にZero Width Non-Joiner(ゼロ幅非接合子, ZENJ)という概念も存在する。符号位置はU+200Cで、HTMLでは‌と記述することもできる。
fl」などデフォルトで合字(fの先端とlが繋がっている)となる文字の並びに対して、「f‌l」というように合字を解除する機能がある。ドイツ語では単語同士を繋げて新たな単語とする複合語が多用されるが、「auf‌lage」=「auf」+「lage」のように「f」と「l」を繋げることが適切でない場合があり、このようなケースに対してZWNJが使われる。
また中東諸言語の文章に対してもZWNJは使われる。

他にもWord Joiner(U+2060)という文字もあり、Word Joinerで結合された文字列は改行によって分⁠断⁠さ⁠れ⁠る⁠こ⁠と⁠が⁠な⁠く⁠な⁠る。(この文中にも使っている)

その他組み合わせで表す文字

  • U+3099・U+309Aはひらがな・カタカナと組み合わせることで濁点・半濁点を付与することができる。
    例: か゚ = U+304B U+309A
  • ヨーロッパの文字でアクセント記号を表す結合文字が存在する。
    例: â = U+0061 U+0302

その他、言語特有の表現が多数存在する。

Unicodeの符号化方式

UTF-16

UTF-16(Unicode Transformation Format 16)はUnicodeの最も基本的な符号化方式。

  • 仕組みは単純で、第0面の文字に対してはUnicodeの符号位置と同じ値の2バイトのbit列がそのままエンコード結果となる。UTF-16の名称はバイト表現が2バイト=16bitであることに由来する。
  • リトルエンディアンビッグエンディアンの違いでUTF-16LE・UTF-16BEのバリエーションがある。LEの場合、Unicodeの符号位置の第1バイトと第2バイトを入れ替えた値がエンコード結果となる。
  • ASCII文字も2バイトになるため互換性がない。
  • 1996年のWindows CEで採用されて以来、Windowsは内部ではUTF-16で文字を処理している。

元々は2バイトの固定長の符号化方式だったが、1996年のUnicode 2.0で追加面(第1面以降)を実装するためにサロゲートペアという仕組みが導入され、固定長ではなくなった。

サロゲートペア

第1面以降(U+10000-)の文字はサロゲートペアという仕組みを使って4バイトで符号化する。

符号化する文字の符号位置を21bitのbit列と見て、(上位5bitの値-1)をyyyy、下位16bitの値をxxxx xxxx xxxx xxxxと置く。そのときエンコード結果は、1101 10yy yyxx xxxx 1101 11xx xxxx xxxx = (0xD800 + yy yyxx xxxx) (0xDC00 + xx xxxx xxxx)となる。
U+D800-U+DBFFは上位サロゲートとして、U+DC00-U+DFFFは下位サロゲートとして予約されているので、x, yにどのような値が来ても他の文字と衝突することはない。

この仕組みによりUnicodeは最大で0b1 0000 1111 1111 1111 1111 = U+10FFFF(第0x10面)まで拡張可能となった。

実際にUTF-16で文字「𝔅」をエンコードする例を見てみよう。
𝔅 = U+1D505 = 0b0 0001 1101 0101 0000 0101
したがって、yyyy = 0000, xxxx... = 1101 0101 0000 0101となる。
よって、UTF-16BEでのエンコード結果は1101 1000 0011 0101 1101 1101 0000 0101 = D8 35 DD 05となる。UTF-16LEの場合は35 D8 05 DD。

エンコードの例

UTF-16BEのエンコード例を挙げる。

文字 符号位置 バイト表現
A U+0041 00 41
U+3042 30 42
𝔅 U+1D505 D8 35 DD 05

UTF-8

UTF-8は1993年に発表されたUnicodeの符号化方式。符号位置によって1バイトから4バイト(6バイト)までの可変長のバイト表現にエンコードする仕組み。
第0面の文字に対して常に2バイトのエンコードを行うUTF-16に対して、UTF-8はASCII文字を1バイトにエンコードするので多くの場合で容量の節約になる。また、ASCIIで書かれた文書はASCIIとしてもUTF-8としてもデコードできるので互換性がある。

具体的には以下のように符号化する。(5バイト・6バイトの符号化はアルゴリズム上可能だがUnicodeでは行われない)

bit数 符号位置 符号化方式
7bit U+0000-U+007F 0xxxxxxx
11bit U+0080-U+07FF 110xxxxx 10xxxxxx
16bit U+0800-U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
21bit U+10000-U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26bit U+200000-U+3FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31bit U+4000000-U+7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

例えば「あ」(U+3042)は表の16bitの部分に当てはまるので、
「あ」 = U+3042 = 0b0011 0000 0100 0010
であることからバイト表現は
11100011 10000001 10000010 = E3 81 82
となる。

エンコードの例

文字 符号位置 bit数 バイト表現
A U+0041 7bit 41
Д U+0414 11bit D0 94
U+3042 16bit E3 81 82
𝔅 U+1D505 21bit F0 9D 94 85

参考

異体字