🧊

文字エンコーディングについてのざっくりまとめ

個人的な理解なので、誤解があるかもしれない・・・ので、間違ってたら教えてください。

書記素クラスタ

日常的に「文字」として認識してるであろうもの。

一般的なエディタでカーソルが動く単位であり、プログラムで文字単位で何かを扱いたいときは、直感的にこれで考えたくなる。

が、ことはそう簡単ではない。

Unicode文字

ユニークなコードポイント(整数値)にマップされた文字の集合であり、主にプログラムで扱うことになるのはこっち。

たとえば、

  • a0x61
  • 👻0x1f47b

というように決まってる。Unicodeにもバージョンがいろいろあるらしい。

書記素クラスタよりも細かい粒度になってるところが重要で、たとえば、🇯🇵👈🏻🧑‍🧑‍🧒みたいなのは、実は複数のUnicode文字の並びでできてる。 (ブラウザで表示すると1書記素に見えてるけど、今このNvimで記事を書いてるときは、複数のUnicodeが並んでるように見えてる。)

なんでユニークなコードポイントが振られてないのか?は、そのほうが柔軟だからとのこと。指差し絵文字が増えるたびに、スキントーンの数だけ枠を定義する必要ある?みたいな。

そういうわけで、Unicode同士をくっつけたり、逆にHTMLのwbr要素みたく非接合を表すUnicode文字なんかもあるとのこと。

エンコーディング

このUnicodeをプログラム上で、メモリとして、どういう単位で表現するか。

  • UTF-8
  • UTF-16
  • UTF-32

概ねこの3パターンが使われるとのこと。

UTF-8

1つのUnicode文字を、8bitで1つから4つまでの可変幅のユニットとして扱う。

ASCIIのようなものなら1byteで足りるし、特殊な絵文字なんかは4byteまで使う。

Rustの&stru8の並びなので、この方式。

UTF-16

JavaScriptはこっち。

こちらも可変幅ではあるが、1つのUnicode文字を、16bitで1つか2つのユニットで扱う。

2ユニット(High + Low)で表現するとき、それらはサロゲートペアと呼ばれ、これはUTF-16ならではの概念。

単一のコードポイントを2つに割ることもできるし、2つのコードユニットからコードポイントに合算することもできる。

UTF-32

すべてを32bit、つまり4byteで扱う。(ただしUnicodeは21bitまでしか使わないらしいけど)

そのため、テキストファイルではサイズが肥大化しがちで、見えるところで使われることはあまりないとのこと。

ちなみにRustで.chars()すると、u8だったバイト列をいい感じに、u32でもあるcharにしてくれて、Unicode文字の単位で扱えるようになって便利。

まとめ

ということを説明した図がWikipediaにあった。

+------+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|      | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |
+------+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|UTF-8 | A  |    Ω    |      語      |        😊         |
|      | 41 | CE | A9 | E8 | AA | 9E | F0 | 9F | 98 | 8A |
+------+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|UTF-16|    A    |    Ω    |    語   |         😊        |
|      |   0041  |   03A9  |   8A9E  |  D83D   |   DE0A  |
+------+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|UTF-32|         A         |         Ω         |         語        |         😊        |
|      |     00000041      |      000003A9     |      00008A9E     |      0001F60A     |
+------+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

Unicode - Wikipedia https://ja.wikipedia.org/wiki/Unicode#%E6%96%87%E5%AD%97%E7%AC%A6%E5%8F%B7%E5%8C%96%E5%BD%A2%E5%BC%8F

書記素クラスタの単位でプログラムから扱うには、一手間が必要。

JavaScriptならIntl.Segmenterを介したり。

Intl.Segmenter - JavaScript | MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter