Pythonのエンコーディングでよくはまるのでメモ
Python温泉に来て以前貰ったReal World Haskellを読み進めています.Haskellとは関係ないですが,さっきUnicodeとかutf-8とかのエラーについてさぼてんの人や@tokibitoさんや@whosaysniさんに教えてもらったのでメモ.
Pythonのstringはbyte列であり,Unicode文字列は内部でどのような表現が使われているかプログラマが意識しなくていいというのがミソだった.どのようなbyte列であるかを指定するにはUnicode文字列を作成するとき*1や,Unicode文字列をPythonの外に出す(端末への出力やファイルへのリダイレクト)ときに指定する必要がある.
具体的には端末上では動いているけれどもファイルへリダイレクトするときにエラーがでるときがある.これは端末はUnicode文字列をどのようなバイト列で出力すればいいかを教えてくれるというのに対してファイルは教えてくれないのでデフォルトのエンコーディングが使われ(多くの場合これはascii), うまくエンコーディングができないのが原因である.
#!/usr/bin/env python # -*- coding: utf-8 -*- # Unicode expression is abstract # Programmer cannot know the implementation(?) # If you omit the 2nd line of this file, # Python cannot know what this byte array(鈴木) means unicode_string = u"鈴木" # When printing, Python sends byte array according it, # only when terminal tells its encoding # But if the output is not terminal or terminal that cannot tell its encoding # Python cannot know what kind of byte array it should send, and fails print(unicode_string) # In Python 2, byte array and string are the same # According their encodings, their inner representation differs utf8_bytearray = unicode_string.encode('utf-8') sjis_bytearray = unicode_string.encode('sjis') print(utf8_bytearray) # utf-8 terminal can print this byte array print(sjis_bytearray) # utf-8 terminal cannot print this print("---------") # These are byte array. Their lengths are not the same print("len(utf8_bytearray): %d" % len(utf8_bytearray)) print("len(sjis_bytearray): %d" % len(sjis_bytearray)) print("---------") unicode_string_from_sjis = sjis_bytearray.decode('sjis') unicode_string_from_utf8 = utf8_bytearray.decode('utf8') # These are unicode string. Their lengths are the same print("len(unicode_string_from_sjis): %d" % len(unicode_string_from_sjis)) print("len(unicode_string_from_utf8): %d" % len(unicode_string_from_utf8))
suztomo@SuzAir.local ~/srm ~/srm $ echo $LANG git[branch:master] ja_JP.UTF-8 suztomo@SuzAir.local ~/srm ~/srm $ ./encode.py git[branch:master] 鈴木 鈴木 --------- len(utf8_bytearray): 6 len(sjis_bytearray): 4 --------- len(unicode_string_from_sjis): 2 len(unicode_string_from_utf8): 2 suztomo@SuzAir.local ~/srm ~/srm $ ./encode.py|hoge.txt git[branch:master] zsh: command not found: hoge.txt Traceback (most recent call last): File "./encode.py", line 15, in <module> print(unicode_string) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
ファイルへのリダイレクトの際に「print(unicode_string)」の行でエラーが起こっている.Unicode文字列をどのようなバイト列で外部に出力すればいいかわからなくなってしまい,デフォルトのasciiのエンコーディングをやろうとしているが失敗している.
いちいち出力する文字列をutf-8を指定するのが面倒なときはPython でUTF-8, shift_jis, euc_jpなど日本語を使う方法のやり方が使えます.
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import codecs sys.stdin = codecs.getreader('euc_jp')(sys.stdin) sys.stdout = codecs.getwriter('shift_jis')(sys.stdout) for line in sys.stdin: print line,