猫(=・ω・=)顔 1.0β

2007-08-08 をのこもすなるぱいそんといふものを

[][] Python日本語を扱う基本をまとめてみるメモ(その2)

「その1」のまとめ

Python で日本語を扱う基本をまとめてみるメモ(その1) - 猫(=・ω・=)顔 1.0β

  • 1行めか2行めで # coding: 〜 を指定する。
  • '<string>' は通常の文字列。保存した形式のままの「バイト列」。
  • u'<string>' は Unicode 文字列。多バイト文字環境で1文字を1文字として扱うために必要。
  • 通常の文字列から Unicode 文字列への変換は unicode() 関数を使う。

今日のお題

unicode() 関数をもうちょっと探る

前回のおさらい。

以下、通常の文字列(バイト列)は、メンドクサイから全部 euc-jp とする。すると、

unimoji = unicode('文字列mojiretuです', 'euc-jp')

で unimoji には、Unicode 文字列になった '文字列mojiretuです' が入る。

さて、なんと、unicode() は第3引数も指定できる。

  • '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

これは何なのかというと、

バイト列を euc-jp としてデコードして Unicode にしたいのにデキネーよ!

って時の挙動なのですが、その時どうするかを第3引数で指定できるのです。

  • 'ignore' は、「まぁ、ちょっとくらい変なのがあっても無視して、できる範囲でデコードしちゃっていいよ」で、
  • 'strict' は、「変なのがあったら即座にエラー人生は厳しい!」

っつー感じ。なので、'strict' の場合は UnicodeDecodeError なるものが出て、エラーの元となったデータの位置が表示されてるのです。

一方、'ignore' の結果を見ると、一見ちゃんと出来てそうだけど、中央の

mojiretu

に着目すると、

mojietu

'r' が無くなってる…! この理由は euc-jp の定義を知っていればわかるでしょう。

つまり、euc-jp デコーダは、

と思うのだけど、2バイト目を見てみたら

  • ガビ〜ン、'r' (= '\x72') かよ…。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」はあるのだろうか…?

(=´ω`=)ノシ

*1:ホントは3バイト目があったり、もっと細かく定義されてるんですが、まぁ、簡単のために端折ります… Wikipedia とか見て欲すぃス…

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証