Pythonのencodeとdecode、strとunicode

pythonで仕事してるとちょいちょいUnicodeEncodeErrorとかUnicodeDecodeErrorに遭遇します。
最初ははまると思うんで原因と対応方法についてメモしておきます。

python2系の話です。

まずstr型とunicode型の違い

pythonのstrはバイト文字列、unicodeユニコード文字列です

# coding=utf-8

s = "あ"
print [s]
# => ['\xe3\x81\x82']
u = u"あ"
print [u]
# => [u'\u3042']

len関数は、str型に対して使うとバイト数を返すので文字数のカウントには使えません
unicode型に対して使うと文字数が返されます

print len(s)
# => 3
print len(u)
# => 1

str型をunicode型に変換するのにdecode関数を使います

ファイルの冒頭でcoding=utf-8を渡しているので、str型のsはutf-8バイトを保持してます。
そのためdecodeにutf-8を渡してます。

decoded_s = s.decode('utf-8')
print type(decoded_s)
# => <type 'unicode'>
print [decoded_s]
# => [u'\u3042']

sjisなどでdecodeしようとするとエラーになります

try:
    s.decode('sjis')
except UnicodeDecodeError, e:
    print e
    # => 'shift_jis' codec can't decode byte 0x82 in position 2: incomplete multibyte sequence

ファイルを読み込んで処理する場合など、どのような文字コードが保持されているかわからない場合は注意が必要です

f = open('/path/to/sjis.txt', 'r')
sjis_content = f.read()
print type(sjis_content)
# => <type 'str'>
decoded_content = sjis_content.decode('sjis')
print decoded_content
# => あ

unicode型をstr型に変換する場合はencodeを使います

encoded_u = u.encode('utf-8')
print type(encoded_u)
# => <type 'str'>
print [encoded_u]
# => ['\xe3\x81\x82']


指定された文字コードに変換出来ない場合、UnicodeEncodeErrorが発生します
以下の場合、①がShift_JISにないのでエラーになってます

u2 = u"\u2460"
print [u2]
# => [u'\u2460']
try:
    u2.encode('sjis')
except UnicodeEncodeError, e:
    print e
    # => 'shift_jis' codec can't encode character u'\u2460' in position 0: illegal multibyte sequence

sjis-2004やutf-8にならencodeすることができます

u2_encoded = u2.encode('sjis-2004')
print type(u2_encoded)
print [u2_encoded]
# => <type 'str'>
# => ['\x87@']

u2_encoded = u2.encode('utf-8')
print type(u2_encoded)
print [u2_encoded]
# => <type 'str'>
# => ['\xe2\x91\xa0']

str型の文字コード変換

str型の文字コードを変換する場合はdecodeしてからencodeすればいいですね

print [sjis_content]
# => ['\x82\xa0']
print [sjis_content.decode('sjis').encode('utf8')]
# => ['\xe3\x81\x82']