2007-08-08 をのこもすなるぱいそんといふものを
■[Python][文字コード] Python で日本語を扱う基本をまとめてみるメモ(その2)
「その1」のまとめ
⇒ Python で日本語を扱う基本をまとめてみるメモ(その1) - 猫(=・ω・=)顔 1.0β
- 1行めか2行めで # coding: 〜 を指定する。
- '<string>' は通常の文字列。保存した形式のままの「バイト列」。
- u'<string>' は Unicode 文字列。多バイト文字環境で1文字を1文字として扱うために必要。
- 通常の文字列から Unicode 文字列への変換は unicode() 関数を使う。
今日のお題
- unicode() 関数をもうちょっと探る
- Unicode 文字列を作る別の方法
- 文字コードの変換方法 (1) Unicode 文字列から任意の encoding へ
- 文字コードの変換方法 (2) encoding-A から encoding-B へ
unicode() 関数をもうちょっと探る
前回のおさらい。
以下、通常の文字列(バイト列)は、メンドクサイから全部 euc-jp とする。すると、
unimoji = unicode('文字列mojiretuです', 'euc-jp')
で unimoji には、Unicode 文字列になった '文字列mojiretuです' が入る。
- 'ignore' または 'strict'
mojiretu = '文字列mojiretuです' u_ignore = unicode(mojiretu, 'euc-jp', 'ignore') print [u_ignore] u_strict = unicode(mojiretu, 'euc-jp', 'strict') print [u_strict]
この場合、2つの unicode() の呼び出しは何も変わらず、u_ignore と u_strict は同じ結果になる。
[u'\u6587\u5b57\u5217mojiretu\u3067\u3059'] ← u_ignore の結果 [u'\u6587\u5b57\u5217mojiretu\u3067\u3059'] ← u_strict の結果
ここで、ちょっと遊んでみよう。(=´ω`;=)ワクワク
mojiretu = '文字列moji\xf0retuです' # ワザと変な \xf0 を入れてみる u_ignore = unicode(mojiretu, 'euc-jp', 'ignore') print [u_ignore] u_strict = unicode(mojiretu, 'euc-jp', 'strict') # ← エラー! print [u_strict]
すると、u_ignore はエラーにならず、u_strict はエラーになる!
[u'\u6587\u5b57\u5217mojietu\u3067\u3059'] ← u_ignore の結果 Traceback (most recent call last): ← u_strict の結果 File "uni.py", line 7, in <module> u_strict = unicode(mojiretu, 'euc-jp', 'strict') UnicodeDecodeError: 'euc_jp' codec can't decode bytes in position 10-11: illegal multibyte sequence
これは何なのかというと、
って時の挙動なのですが、その時どうするかを第3引数で指定できるのです。
っつー感じ。なので、'strict' の場合は UnicodeDecodeError なるものが出て、エラーの元となったデータの位置が表示されてるのです。
一方、'ignore' の結果を見ると、一見ちゃんと出来てそうだけど、中央の
mojiretu
に着目すると、
mojietu
'r' が無くなってる…! この理由は euc-jp の定義を知っていればわかるでしょう。
と思うのだけど、2バイト目を見てみたら
と言って怒る (strict) か、そもそも '\xf0\x72' を無視する (ignore)。なので、ignore では 'r' も無くなってしまう。けど、まぁなんとなく結果が出てくる。
unicode() の第3引数は 'strict' がデフォルト。世の中はキビシーのだと教えてくれる。でも、入力文字列がおかしいときはちゃんとエラーにして欲しいので、デフォルトが 'strict' なのは良いことでしょう。スルー力が高くていいときにだけ、自分で 'ignore' を明示するのです。
Unicode 文字列を作る別の方法
unicode() 関数を使わずとも、「通常の文字列のメソッド」として
- decode()
というのが用意されている。
mojiretu = '文字列mojiretuです' uniuni = unicode(mojiretu, 'euc-jp')
の代わりに
mojiretu = '文字列mojiretuです' uniuni = mojiretu.decode('euc-jp')
と書いても、同じく Unicode 文字列を作ってくれる。decode() にもやはり unicode() と同じく
mojiretu = '文字列moji\xf0retuです' u_ignore = mojiretu.decode('euc-jp', 'ignore') u_strict = mojiretu.decode('euc-jp', 'strict')
'ignore' か 'strict' を指定できる。結果も一緒なのでご安心を。
decode() を使う方がなんとなく見た目がスマートか。気分の問題か好き嫌いの問題か。
文字コードの変換方法 (1) Unicode 文字列から任意の encoding へ
unicode('<string>') や '<string>'.decode() は、 任意のエンコーディング方式の文字列を Unicode 文字列に変換してくれた。
さて、その逆は?
u'<unicode_string>'.encode(<encoding>)
Unicode 文字列のメソッドとして定義されている
- encode()
を使えばよろしい。これで、Unicode 文字列を好きな文字コードに変換できる。
mojiretu = '文字列mojiretuです' uniuni = mojiretu.decode('euc-jp') print [uniuni.encode('euc-jp')] print [uniuni.encode('iso-2022-jp')] print [uniuni.encode('shift_jis')] print [uniuni.encode('utf-8')] print [uniuni.encode('utf-16')] print [uniuni.encode('utf-16le')] print [uniuni.encode('big5')] # 繁体字中国語 print [uniuni.encode('gb2312')] # 簡体字中国語 print [uniuni.encode('euc-cn')] # 中国語EUC (=gb2312) print [uniuni.encode('euc-kr')] # 韓国語EUC
もう、何でもござれw。結果はこうでした。
['\xca\xb8\xbb\xfa\xce\xf3mojiretu\xa4\xc7\xa4\xb9'] ← euc-jp ['\x1b$BJ8;zNs\x1b(Bmojiretu\x1b$B$G$9\x1b(B'] ← iso-2002-jp ['\x95\xb6\x8e\x9a\x97\xf1mojiretu\x82\xc5\x82\xb7'] ← shift_jis ['\xe6\x96\x87\xe5\xad\x97\xe5\x88\x97mojiretu\xe3\x81\xa7\xe3\x81\x99'] ← utf-8 ['\xff\xfe\x87eW[\x17Rm\x00o\x00j\x00i\x00r\x00e\x00t\x00u\x00g0Y0'] ← utf-16 ['\x87eW[\x17Rm\x00o\x00j\x00i\x00r\x00e\x00t\x00u\x00g0Y0'] ← utf-16le ['\xa4\xe5\xa6r\xa6Cmojiretu\xc6\xcb\xc6\xbd'] ← big5 ['\xce\xc4\xd7\xd6\xc1\xd0mojiretu\xa4\xc7\xa4\xb9'] ← gb2312 ['\xce\xc4\xd7\xd6\xc1\xd0mojiretu\xa4\xc7\xa4\xb9'] ← euc-cn ['\xd9\xfe\xed\xae\xd6\xaamojiretu\xaa\xc7\xaa\xb9'] ← euc-kr
じゃあ、ここで、
print [uniuni.encode('iso-8859-1')] # Latin 1
とかやってみる。これは…
Traceback (most recent call last): File "uni.py", line 18, in <module> print [uniuni.encode('iso-8859-1')] UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-2: ordinal not in range(256)
エラー。なぜなら、'文字列mojiretuです' には iso-8859-1 (Latin 1) で表現できない文字(日本語の文字)が含まれているから。
それなら、「もしや!」と思い、
print [uniuni.encode('iso-8859-1', 'ignore')]
とすると、今度はエラーにならず、
['mojiretu']
となった。つまり、unicode() や decode() と同じく encode() も 'strict' がデフォルトで、'ignore' も指定できる。そしてこの時、「文字列」と「です」は華麗にスルーされたのであった。
文字コードの変換方法 (2) encoding-A から encoding-B へ
というわけで、Unicode 文字列から任意のエンコーディング方式に変換できることはわかった。
では、任意のエンコーディング方式から任意のエンコーディング方式に変換するにはどうするの?
sjis_mojiretu = euc_mojiretu.decode('euc-jp').encode('shift_jis')
これでよい。処理としては、
- 最初に euc_mojiretu が euc-jp 形式で存在している
- euc_mojiretu.decode('euc-jp') でそれを Unicode 文字列にする
- さらにその Unicode 文字列を encode('shift_jis') で shift_jis にする
- それを sjis_mojiretu に入れる
ということになる。つまり、Unicode 文字列を介して euc-jp から shift_jis に変換してる。
もちろん、
sjis_mojiretu = unicode(euc_mojiretu, 'euc-jp').encode('shift_jis')
ともかける。でも、前者の方が
ような気分。ま、どちらでも。でも、unicode() を使うか '〜'.decode() を使うかは統一した方がいいかもしれない。
というわけで
「その2」は終了。果たしてこのシリーズに「その3」はあるのだろうか…?
(=´ω`=)ノシ