定期的にこの手の記事が上がってる気がするけどあげていく。
unicode型とかstr型とかいう言い方するとわかりにくいけど、データの実体がどういうものかわかってれば理解しやすいよね。
参考
- Python - ドキュメント - Unicode HOWTO
- プログラマのための文字コード技術入門
- Unicode のサロゲートペアとは何か
- Wikipedia - UTF8
- Wikipedia - ASCII
Unicode
Unicode では全ての文字にID(コードポイント)(0 ~ 0x10FFFF)をふっている。コードポイントを表す時は U+{16進数} と書く。
>>> ord("A") 65 >>> hex(ord("A")) '0x41' >>> chr(ord("A")) 'A'
ちなみに各コードポイントは文字に必ずしも割り当てられているわけではない。サロゲートコードポイントなどがその一例だが今回はその説明については省略する。
文字符号化方式
Unicodeにより各文字にはそれぞれコードポイントが割り当てられいる、それではこれをコンピュータによってどのように表現するかであるが、 UTF-8 UTF-16 UTF-32 といった符号化方式が存在する。それぞれ 8bit 16bit 32bit を単位とする符号化方式である。
UTF-16
16bitで表すことのできる U+0000 ~ U+FFFF までは1符号単位(16bit)により表す。
16bitだけで表現できない U+10000 ~ U+10FFFF はサロゲートペアという二つの符号単位の塊(16bit * 2)により表現する。
サロゲートペアを構成するコードポイントはサロゲートコードポイントと呼ばれる、サロゲートコードポイントはさらに以下のように分けることができて
上位サロゲートと下位サロゲートによりサロゲートペアを構成する
UTF-8
8bitを符号単位とする符号化方式 1byteで表現できる U+0000 ~ U+007F は1byte 2byteで表現できる U+0080 ~ U+07FF は2byte というようにコードポイントによって異なる長さのバイトシーケンスによりいい感じに符号化を行う
ASCII
ASCIIは7bitにより表される各整数(0~127)に対して文字を割り当てた符号化方式
ord()
により得られる整数(0~127)は Unicode code point でありかつ ascii code でもある。
unicode code point 0~127 の文字に関しては Unicode と ascii は互換性がある。
Python3 における str型 と bytes型
ここまできたら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> -*-
UnicodeDecodeError/UnicodeEncodeError
バイトシーケンス => 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型 と unicode型
Python2におけるstr型はPython3のそれとは違う
混乱するね
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 のデフォルトエンコーディング
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)