個人的な理解なので、誤解があるかもしれない・・・ので、間違ってたら教えてください。
書記素クラスタ
日常的に「文字」として認識してるであろうもの。
一般的なエディタでカーソルが動く単位であり、プログラムで文字単位で何かを扱いたいときは、直感的にこれで考えたくなる。
が、ことはそう簡単ではない。
Unicode文字
ユニークなコードポイント(整数値)にマップされた文字の集合であり、主にプログラムで扱うことになるのはこっち。
たとえば、
a
は0x61
👻
は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の&str
はu8
の並びなので、この方式。
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