tanihito’s blog

デジタル・新規事業開発・健康など、興味のあることについてつらつらと書いてきます。

Pythonの文字列の話

エキスパートPythonプログラミング読書会19に参加して、Unicodeの話を聞いてきました。自分が理解できた範囲でメモしておきます。decode(), encode(), io.open()を使いこなせるようになれば、UnicodeErrorに困ることも減るのでは、と感じました。

文字列にはstr型 (バイト文字列) とunicode型 (ユニコード文字列) の2種類がある。

>>> type('こんにちは')
<type 'str'>
>>> type(u'こんにちは')
<type 'unicode'>

str型の文字列は思い通りの文字列処理ができない。unicode型を使うべき。

>>> len('こんにちは')
15
>>> len(u'こんにちは')
5

str型→unicode型には、decode()を、unicode型→str型にはencode()を使う。

>>> u = 'こんにちは'.decode('utf-8')
>>> type(u)
<type 'unicode'>
>>> s = u'こんにちは'.encode('utf-8')
>>> type(s)
<type 'str'>

decode()とencode()を間違えるとエラーになる。

>>> 'こんにちは'.encode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)
>>> u'こんにちは'.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128) 

unicode型の内部表現にはUTF-16UTF-32がある。sys.maxunicodeが65536ならUTF-16、1114111ならUTF-32

>>> import sys
>>> sys.maxunicode
1114111

str型とunicode型の変換でデフォルトで使用されるエンコーディングはascii。

>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> u = 'こんにちは'.decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)

デフォルトエンコーディングを変更できるが、問題の原因を見つけにくくなるのでやらない

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>> sys.getdefaultencoding()
'utf-8'
>>> u = 'こんにちは'.decode()
>>> type(u)
<type 'unicode'>

io.open()を使うと、unicode型で読み書きできる。

>>> f = open('foo.txt')
>>> line = f.readline()
>>> type(line)
<type 'str'>
>>> import io
>>> f = io.open('foo.txt')
>>> line = f.readline()
>>> type(line)
<type 'unicode'>

Python3では文字列がunicode型にまとめられ、文字列処理が簡単になった。しかし、まだPython3に対応していないライブラリも多い。どのライブラリが対応しているかはPython 3 Support on PyPIから確認できる。