Chapter 01

Unicode と UTF-8

ほげさん
ほげさん
2023.02.28に更新

このページでは 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 のエンコーディング結果はバイト数に応じて必ず 01 にならなければいけないビットが決まっている。

3 バイトなら ↓ のようにならなければいけないし、
1110**** 10****** 10******

1 バイトなら ↓ のようにならなければいけない。
0*******

たとえば を UTF-8 でエンコーディングするには、次のような手順になる。

  1. Unicode で に対応するコードポイントを得る ( U+3042 )
  2. 3042 を 16 進数から 2 進数にする ( 0011000001000010 )
  3. 定められたビットを除く * の部分にこの 2 進数を当てはめる
  4. 16 進数にする

この手順に則らずに強引にバイト列を作ったとしても、不正なビットを含んでいる場合は UTF-8 のバイト列とはみなされない。

また Unicode で定められている文字は U+0000 ~ U+10FFFF の範囲なので、正しい手順で変換したとしてもコードポイントが上限値 ( 111 万 4111 ) を超えている場合は UTF-8 のバイト列とみなされない。

整理

  • Unicode は扱える文字を定義したもの
  • UTF-8 は Unicode のコードポイントを変換するルール
  • 全てのバイト列が UTF-8 として有効なわけではない
  • UTF-8 以外に UTF-16 などもある

参考

https://wa3.i-3-i.info/word1812.html
https://ferret-plus.com/7006
https://uxmilk.jp/45155
https://marunouchi-tech.i-studio.co.jp/1937/
https://qiita.com/nomaton/items/04e90c02fbbbffa7ee78