トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

エンコードとデコード.py

[Python]
Pythonでは、OS上で扱われている具体的な文字コードの文字列を、Python内部形式のユニコード文字に変換することを、「エンコード」という。
エンコードされた文字を、具体的な文字コードの文字列に変換することを、「デコード」という。

具体的な文字コードとは、ASCIIとか、Shift JISとかEUCとかUTF-8とかのこと。

以下、Python 3ではまったく変わってしまったので書き直す。
Python 3の文字列はすべて、エンコードされたUnicode文字列として、Pythonインタープリターが保持している。

なので、そもそもエンコード処理が成功していなければ、そのデータを文字列としてPythonプログラムが保持することさえ、できなくなっている。
エンコードするには、Pythonインタープリターが、そのデータの文字コードを正確に知っていなければならない、ということになる。

そうだとすると、文字列リテラルとか、実行中に渡される引数とか標準入出力とかにおいて、正しい文字列がわかっていないとエラーになってしまう、ということだ。

Python 3は、デフォルトでソースコード中の文字列リテラルはUTF-8であるとみなす。そうでないとSyntax Errorを吐く。
恐ろしいことだ。

# encoding: utf-8

じゃあ標準入出力はどうしているか?
おそらくだが、sys.stdin.encoding、sys.stdout.encoding、sys.getfilesystemencoding、sys.getdefaultencodingなどを使っていると思われる。
その前提が違っている文字が渡されると、UnicoeDecodeErrorが吐かれるはずだ。

# リテラル文字列からバイト型データを作る。
# encodingを指定しないと、デフォルトのencodingが使われる、とのこと。
# Pythonのユニコード文字列は、Pythonが扱える任意の型のバイト型データに変換可能。
b1 = bytes("ほげほげ", encoding='cp932')
b2 = "ほげほげ".encode('utf-8')

# バイト型データは、特定のエンコード型を持つことができる。すなわちそれは、Python 2までの文字列。またはいわゆる普通のコンピューターのエンコードされた文字列。
# Pythonで文字列データを扱うには、エンコードされたバイト型データをPython 3文字列(Unicode型)にしないとならない。これがデコード処理。
# デコード処理を行うためには、元のバイト型データがどんなencodingなのか正しく指定しないと、UnicodeDecodeErrorが発生する。
d1 = b1.decode('cp932')
d2 = str(b2, encoding='utf-8')

----- 旧 -----
ユニコード文字列にデコードするメリットとしては、Pythonがマルチバイト文字を扱うことができるようになる、ということ。
プログラマが文字コードを意識した処理を書くことで、意図しない文字コードの欠損や変換(いわゆる文字化け)が生じないプログラムを書く、といったことになる。

Pythonユニコード文字列にデコードを行うとき、プログラマは元の文字列の文字コードが何であるのか、正しく知っていなければならない。
しかしこれは、しばしば問題を伴う。
日本語ではメジャーな文字コードがShift JIS、EUC、UTF-8と複数あるため、ユーザからの入力文字列を受け取るプログラムは、容易に正しい文字コードを特定できない状況で使用される場合があるということ。

PerlのEncode.pmのような標準モジュールを持たないPythonでは、根本的な解決策はない。

コンソール上でのみ使用されるプログラムの場合、入力文字の文字コードを推測する方法として、

import sys
decoded_string1 = unicode(input_string1, sys.getfilesystemencoding())
decoded_string2 = unicode(input_string2, sys.stdin.encoding)

というのがある。後者はコンソールから入力された文字の文字コードとしてはかなり信憑性がある。
しかしOSのファイルシステム上に存在しているファイルの中身が、すべてsys.getfilesystemencoding()で解決できるかと言うと、そうは言えない場合もある。

デコード時の文字コード指定が間違っていると、Pythonではほぼ100%例外が発生する。
効率は悪いが、デフォルトの文字コードでデコードできない場合は、try: except:で可能性のある文字コードを全部試して行く、という方法もある。

一番困るのはCGIのようなもので、PerlだとEncode.pmで文字コードをguessできるが、Pythonには標準ではそのようなものはない(というか、標準でマルチバイト文字コードのguessができるなんて、Perlくらいのもんだ)。

自分で実装する、どっかから拾ってくる、nkfのようなOS上の仕組みを使う、といった苦しい逃げを打たなければならない。

Pythonユニコード文字列(デコードされた文字列)の標準出力への出力は単純で、print文はsys.stdout.encodingにエンコードして出力を行う。
sys.stdout.encoding以外の文字コードで標準出力に書き出したら、文字化けするので、これは当然の動作と言えば当然。

ファイルに書き出す場合は、プログラマが好きな文字コードにエンコードすればよい。