https://docs.python.org/ja/3.13/howto/unicode.html
Unicode ではすべての文字 (Character) を対象にしており、各文字に codepoint を設定しています。これは各文字に一意の数字を割り当てるもの、といえます。
265E '♞'; BLACK CHESS KNIGHT
265F '♟'; BLACK CHESS PAWN
U+265E
は Unicode 上のコードポインタが 265E であることを表しています。
また、 0x265e = 9,822 が '♞'; BLACK CHESS KNIGHT
になっています。
エンコーディング
Unicode のコードポイントの列は codeunit 列として表され、 8-bit のバイト列にマップされます。
上記の画像では 32bit = 4 byte で表されており、 P =
0x00000050 = 80
です。
#little-endian で扱われるため 0x50000000 となります。
ただし、多くの文書は 127 ~ 255 の alphabet で表せるため 0x00 の 1byte で表現できるはずですが、上記の例では 1 文字 4 byte となっています。 これだと効率が悪いです。
そのため、 UTF-8 という Unicodde の encoding が使われます。 UTF-8 の仕組みは
- コードポイントが 128 未満であれば、対応する 1 バイトで表現する
- コードポイントが 128 以上であれば、 128 ~ 255 までのバイトからなる 2, 3, 4 バイトのシーケンスで表現する
です。 UTF-8 という 8 bit を基本としたエンコーディングであれば最小限のバイトで同一の英文列を表現できます。
Python における Unicode
Python ソースコードのデフォルトエンコーディングは UTF-8 です。 そして、 python の str は Unicode で表されます。
ord を使うと codepoint に変換でき、また chr で Unicode 文字に変換できます。 ✅️ = 9989 = 0x2705 = U+2705 です。
>>> ord('✅')
9989
>>> chr(9989)
'✅'
str.encode()
メソッドによって、 Unicode 文字列を指定された encoding でエンコードして bytes に変換します。
str 型から byte 型へエンコーディングしたうえで変換するのですね。
>>> txt='ab✅️'
>>> txt.encode('utf-8')
b'ab\xe2\x9c\x85\xef\xb8\x8f'
>>> txt.encode('utf-16')
b"\xff\xfea\x00b\x00\x05'\x0f\xfe"
>>> txt.encode('utf-32')
b"\xff\xfe\x00\x00a\x00\x00\x00b\x00\x00\x00\x05'\x00\x00\x0f\xfe\x00\x00"
utf-8 のときは b'ab\xe2\x9c\x85\xef\xb8\x8f'
となっています。これは
- a = U+0061 = b’a’ (0x61)
- b = U+0062 = b’b’ (0x62)
- ✅️ = U+2705 = \xe2\x9c\x85 (0xE29C85)
- ” (絵文字制御専用文字) = U+FE0F = \xef\xb8\x8f
よって b'ab\xe2\x9c\x85\xef\xb8\x8f'
で表されます。
utf-16 のときは
- BOM = リトルエンディアンであることを表す = \xff\xfe
- 下位ビットから上位ビットへ
- a = U+0061 = 0x61 0x00 = a\x00
- b = U+0062 = 0x62 0x00 = b\x00
- ✅️ = U+2705 = 0x05 0x27 = \x05’
- ” = U+FE0F = \x0f\xfe
となります。
U+2705 が \xe2\x9c\x85 になる理由
U+2705 が \xe2\x9c\x85 になる理由は以下です。
U+2705 は 3 byte であり, 3 byte は
- 1 byte =
1110xxxx
- 2 byte =
10xxxxxx
- 3 byte =
10xxxxxx
というフォーマットで表されます。これは UTF-8 で定められたルールです。
0xE2, 0x9c, 0x85 を 2 進数に純粋に変換すると
- 0xE2 =
11100010
- 0x9C =
10011100
- 0x85 =
10000101
になります。
そして、 1, 2, 3byte 目のルール 1110xxxx 10xxxxxx 10xxxxxx で 0xE2, 0x9c, 0x85 を抜き出すと
- 0010
- 011100
- 000101
となります。 0010 011100 000101 (16bit) を 16 進数に直すと
- 0010 = 2
- 0111 = 7
- 0000 = 0
- 0101 = 5
となり、2705 に一致します。
U+2705 からバイトに変換したい場合は
- 2705 → 0010 0111 0000 0101 に変換する
- 0x0800 ~ 0xFFFF の範囲内であるため 3 byte で表現する方針になる
1110xxxx
, 10xxxxxx, 10xxxxxx のテンプレートに割り当てる- 1byte 目: 1110 0010 (上位 4bit) = 0xE2
- 2byte 目: 10 0111100 = 0x9C
- 3byte 目: 10 000101 (下位 6 bit) = 0x85
よって、\xE2\x9C\x85 となります。