このページでは Rust の文字列型に対する前提知識を得るために Unicode と UTF-8 などについてまとめる。
Unicode と UTF-8
コンピュータは 0 と 1 しか扱えないため、人間が使う文字は 2 進数に変換しなければならない。
この変換は「符号化文字集合」と「文字符号化方式」の 2 つによって段階的に行われる。
前者は「扱える文字をコードで定義したまとめ」で後者は「変換ルール」と考えていい。
簡単にたとえるなら ↓ のような感じになる。
Unicode は符号化文字集合で UTF-8 は文字符号化方式なので、同じようにたとえると ↓ のようになる。
余談 Shift_JIS
Unicode のほかにも JIS X 0208 などの符号化文字集合もある。
これは日本の地名や人名を表記することが目的で、7,000 文字くらいがまとめられている。
JIS X 0208 に使う変換ルールが Shift_JIS だ。
UTF-8 と UTF-16
Unicode に対応する変換ルールは UTF-8 だけでなく、UTF-16 という方式もある。
文字符号化方式が異なるので、同じ符号化文字集合を使っているが UTF-8 と UTF-16 のエンコード結果は異なる。
UTF-8 も UTF-16 も基本的に Unicode の文字を全て変換できるが、エンコーディングの方式や主に利用されるプラットフォームが違う。
UTF-8
- 可変長の変換方式であり、文字によって使用するメモリ量が変わる
- あ → E3 81 82
- a → 61
- ! → 21
- Unix 系 OS や Web システムで主に利用される
UTF-16
- 固定長の変換方式であり、使用するメモリ量は文字数に比例するので扱いやすい
- あ → 30 42
- a → 00 61
- ! → 00 21
- Windows で利用されるほか、Java の
String
の保持するバイト列は UTF-16 である
UTF-8 として不正なバイト列
細かいことは割愛するが、UTF-8 のエンコーディング結果はバイト数に応じて必ず 0
や 1
にならなければいけないビットが決まっている。
3 バイトなら ↓ のようにならなければいけないし、
1110**** 10****** 10******
1 バイトなら ↓ のようにならなければいけない。
0*******
たとえば あ
を UTF-8 でエンコーディングするには、次のような手順になる。
- Unicode で
あ
に対応するコードポイントを得る (U+3042
) -
3042
を 16 進数から 2 進数にする (0011000001000010
) - 定められたビットを除く
*
の部分にこの 2 進数を当てはめる - 16 進数にする
この手順に則らずに強引にバイト列を作ったとしても、不正なビットを含んでいる場合は UTF-8 のバイト列とはみなされない。
また Unicode で定められている文字は U+0000 ~ U+10FFFF
の範囲なので、正しい手順で変換したとしてもコードポイントが上限値 ( 111 万 4111 ) を超えている場合は UTF-8 のバイト列とみなされない。
整理
- Unicode は扱える文字を定義したもの
- UTF-8 は Unicode のコードポイントを変換するルール
- 全てのバイト列が UTF-8 として有効なわけではない
- UTF-8 以外に UTF-16 などもある
参考