概要
- Unicode のことを調べて、何かわかった。
- UTF-8 のことを調べて、何かわかった。
- 試しに「あ」を手動で UTF-8 エンコードしてみようと思った。
そういうことになった。
Unicode
Unicode は全世界の文字の 文字:コードポイント
セットのこと。一覧表は Wikipedia にありました。
Python では以下のように変換できます。
# ord: 文字 -> コードポイント
ord('あ') # 12354
# chr: コードポイント -> 文字
chr(12354) # 'あ'
UTF-8
上述した Unicode コードポイントを、特定のルールによって、バイト列に変換する方式です。バイト列については先日調べました。(バイト列ってなんなの)
Python では以下のように変換できます。
'あ'.encode('UTF-8') # b'\xe3\x81\x82'
特定のルールとは
- まず文字のコードポイントを取得する。
- つぎに、以下のパターンに従ってバイト列(8桁2進数の連なり)をつくる。
コードポイントの値 | バイト列の作り方 |
---|---|
7f(127) まで |
コードポイントを7桁の2進数にし、0xxxxxxx に当てはめる。 |
7ff(2047) まで |
コードポイントを11桁の2進数にし、110xxxxx 10xxxxxx に当てはめる。 |
ffff(265535) まで |
コードポイントを16桁の2進数にし、1110xxxx 10xxxxxx 10xxxxxx に当てはめる。 |
10ffff(1114111) まで |
コードポイントを21桁の2進数にし、11110xxx 10xxxxxx 10xxxxxx 10xxxxxx に当てはめる。 |
- そして、完成したバイト列を16進数化することでエンコードの完了。
「あ」
Python で 'あ'
の UTF-8 エンコードを再現してみます。
# 文字のコードポイントを取得します。 'あ' -> 12354
codepoint = ord('あ')
# 12354 だと「ffff まで」の分類になるので
# コードポイントを16桁の2進数にし、 1110xxxx 10xxxxxx 10xxxxxx に当てはめます。
# コードポイントを16桁の2進数にします。 12354 -> '0011000001000010'
codepoint_bin = '{:016b}'.format(codepoint)
# x に2進数を当てはめます。
bytes_bin = [
'1110' + codepoint_bin[ : 4], # '1110xxxx' -> '11100011'
'10' + codepoint_bin[ 4:10], # '10xxxxxx' -> '10000001'
'10' + codepoint_bin[10: ], # '10xxxxxx' -> '10000010'
]
# 各バイト列を16進数に変換します。
def hex_(b):
return '{:02x}'.format(int(b, 2))
bytes_hex = map( hex_, bytes_bin ) # '11100011' '10000001' '10000010' -> 'e3' '81' '82'
# くっつけます。
bytes_ = ''.join(bytes_hex) # 'e3' '81' '82' -> 'e38182'
# bytes 型にします。
bytes_ = bytes.fromhex(bytes_) # 'e38182' -> b'\xe3\x81\x82'
# 本当に UTF-8 になっているのか? デコードして確認してみよう。
print( bytes_.decode('UTF-8') ) # b'\xe3\x81\x82' -> 'あ'
やったあ。できました。このコードは、 'あ'
でエンコードを試す実験なので汎用性はもちろんありません。