文字コードの話 (3) - Unicode
文字コードについて調べたことをまとめます。
関連記事
- 文字コードの話 (1) - ASCII
- 文字コードの話 (2) - Shift_JIS
- 文字コードの話 (3) - Unicode (本記事)
- 文字コードの話 (4) - UnicodeのEmoji
- 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が繋がっている)となる文字の並びに対して、「fl」というように合字を解除する機能がある。ドイツ語では単語同士を繋げて新たな単語とする複合語が多用されるが、「auflage」=「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 |
参考
- Unicodeとは? その歴史と進化、開発者向け基礎知識 - Build Insider
- (プログラマのための) いまさら聞けない標準規格の話 第1回 文字コード概要編 | オブジェクトの広場
- 文字列とUnicode · JavaScript Primer #jsprimer