この文字列を utf-8 としてデコードしようとすると、ご存知のように、これらの偽の cp1252 文字は無効な utf-8 であるため、「UnicodeDecode」エラーが発生します -
ただし、Python コーデックでは、codecs.register_error 関数を使用して、エンコード/デコード エラーを処理するコールバックを登録できます。これは UnicodeDecodeerror aa パラメータを取得します。データを「cp1252」としてデコードしようとするようなハンドラを作成できます。文字列の残りの部分について、utf-8 でのデコードを続行します。
私の utf-8 端末では、次のように正しくない文字列が混在する可能性があります。
>>> a = u"maçã ".encode("utf-8") + u"maçã ".encode("cp1252")
>>> print a
maçã ma��
>>> a.decode("utf-8")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 9-11: invalid data
上記のコールバック関数をここに記述し、キャッチを見つけました。文字列をデコードする位置を1ずつインクリメントしても、次の文字もutf-8ではない場合、次のchratcerで開始されるようになります。範囲外 (128) の場合、最初の範囲外 (128) 文字でエラーが発生します。つまり、ASCII 以外、UTF-8 以外の文字が連続して見つかった場合、デコードは「戻ります」。
これを回避するには、この「ウォークバック」を検出し、最後の呼び出しからデコードを再開する error_handler に状態変数を設定します。この短い例では、グローバル変数として実装しました (手動で行う必要があります)。デコーダーへの各呼び出しの前に「-1」にリセットします):
import codecs
last_position = -1
def mixed_decoder(unicode_error):
global last_position
string = unicode_error[1]
position = unicode_error.start
if position <= last_position:
position = last_position + 1
last_position = position
new_char = string[position].decode("cp1252")
#new_char = u"_"
return new_char, position + 1
codecs.register_error("mixed", mixed_decoder)
そしてコンソールで:
>>> a = u"maçã ".encode("utf-8") + u"maçã ".encode("cp1252")
>>> last_position = -1
>>> print a.decode("utf-8", "mixed")
maçã maçã