Pythonは文字コードが闇とよく言われるけど、文字コードについて正しく理解していればそんなに難しくはないと思う。 ドキュメントを読めば全部書いてあるけど長い。インターネットに転がっているブログでは「unicode文字列は、文字列をunicode型で保持する」というような説明になっていないようなブログや「u'....' のように、頭に"u"をつけて、 この文字列がUTF-8で書かれている事を明言」など間違ったことを書いているエントリが多い(しかもそれが人気記事だったりするしどうなっているんだ) ということでざらっと説明することにした。詳しくはやっぱり公式ドキュメントを読もう。
Unicode では全ての文字にID(コードポイント)(0 ~ 0x10FFFF)をふっている。コードポイントを表す時は U+{16進数} と書く。 ord()により、ある文字に対応するコードポイント(の10進数表記を得ることができる) chr()により、あるコードポイント(の10進数整数)から対応する文字を得ることができる
>>> ord("A")
65
>>> hex(ord("A"))
'0x41'
>>> chr(ord("A"))
'A'
ちなみに各コードポイントは文字に必ずしも割り当てられているわけではない。サロゲートコードポイントなどがその一例だが今回はその説明については省略する。
Unicodeにより各文字にはそれぞれコードポイントが割り当てられいる、それではこれをコンピュータによってどのように表現するかであるが、 UTF-8 UTF-16 UTF-32 といった符号化方式が存在する。それぞれ 8bit 16bit 32bit を単位とする符号化方式である。
16bitで表すことのできる U+0000 ~ U+FFFF までは1符号単位(16bit)により表す 16bitだけで表現できない U+10000 ~ U+10FFFF はサロゲートペアという二つの符号単位の塊(16bit * 2)により表現する サロゲートペアを構成するコードポイントはサロゲートコードポイントと呼ばれる、サロゲートコードポイントはさらに以下のように分けることができて
- 上位サロゲート: U+D800〜U+DBFF
- 下位サロゲート: U+DC00〜U+DFFF
上位サロゲートと下位サロゲートによりサロゲートペアを構成する
8bitを符号単位とする符号化方式 1byteで表現できる U+0000 ~ U+007F は1byte 2byteで表現できる U+0080 ~ U+07FF は2byte というようにコードポイントによって異なる長さのバイトシーケンスによりいい感じに符号化を行う
ASCIIは7bitにより表される各整数(0127)に対して文字を割り当てた符号化方式
127)は Unicode code point でありかつ ascii code でもある。
unicode code point 0~127 の文字に関しては Unicode と ascii は互換性がある。ord()
により得られる整数(0
ここまできたらPythonにおけるunicode型とstr型が理解できると思う。
Python3では str型
をUnicode文字(Unicode code point のシーケンスで表された文字)を実態としている。
それに対して bytes型
はutf-8やutf-16やasciiといった符号化方式でエンコードされたバイトシーケンスが実態
>>> type("🍣")
<class 'str'>
>>> hex(ord("🍣"))
'0x1f363' # Unicode code point
>>> "🍣".encode('utf-8') # unicode code point を utf-8 でエンコード
b'\xf0\x9f\x8d\xa3' # バイトシーケンス
>>> type(b'\xf0\x9f\x8d\xa3')
<class 'bytes'>
>>> b'\xf0\x9f\x8d\xa3'.decode('utf-8') # バイトシーケンスをutf-8だと思い込んで、unicode code point にデコード
'🍣'
Python3でのデフォルトエンコーディングはUTF-8 バイトシーケンス<--->Unicode code point のエンコード/デコードはデフォルトではutf-8で行われる
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'
ファイルの先頭で以下のようにエンコーディングを指定することもできる
# coding: -*- <encoding name> -*-
バイトシーケンス => Unicode code point への変換を考えてみる
# ascii互換の文字(code point 0~127 の文字)は utf8 バイトシーケンス を
# ascii バイトシーケンスだと思って decode しても unicode code point が得られる
# 0~127 の文字はutf-8でも 1byte だし (128~255 はasciiではない...)
>>> 'A'.encode('utf-8').decode('ascii')
'A'
# ascii非互換の文字のutf-8バイトシーケンス取得
>>> 'あ'.encode('utf-8')
b'\xe3\x81\x82'
# これをasciiだと思ってDecodeを試みる
# \xe3 などは 0~127 外(非ascii) なのでDecodeに失敗する(UnicodeDecodeError)
>>> 'あ'.encode('utf-8').decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position0: ordinal not in range(128)
次に Unicode code point => ascii バイトシーケンスへの変換を考えてみる
# Unicode code point (str型) をバイト列(ascii encode)に変換する際
# コードポイントが0~127ならOK
# それ以外ならascii encode に失敗 (UnicodeEncodeError)
>>> 'A'.encode('ascii')
b'a'
>>> 'あ'.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u3042' in position 0: ordinal not in range(128)
Python2におけるstr型はPython3のそれとは違う
- Python2のstr型 = Python3のbytes型 = バイトシーケンス
- Python2のunicode型 = Python3のstr型 = Unicode code point シーケンス
混乱しないでください
Python3では 'a'
といった表記の文字列は Unicode code point で表されていたが、
Python2では 'a'
はバイトシーケンスで表される。
Python2でユニコードコードポイントを得るには u'a'
このように書く
>>> import sys
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=13, releaselevel='final', serial=0)
>>> type('a')
<type 'str'> # ここで言うstrはバイトシーケンスであることに注意
>>> type(u'a')
<type 'unicode'> # これは unicode code point
Python2 でのデフォルトエンコーディングはascii
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
なので
# python3 ではこれは utf-8 エンコード、デコードだが
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=1, releaselevel='final', serial=0)
>>> 'あ'.encode()
b'\xe3\x81\x82'
>>> b'\xe3\x81\x82'.decode()
'あ'
>>> import sys
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=13, releaselevel='final', serial=0)
# unicode code point を ascii エンコードしようとする、「あ」はasciiの範囲外
>>> u'あ'.encode()
UnicodeEncodeError: 'ascii' codec can't encode character u'\u3042' in position 0: ordinal in range(128)