7

ASCII ではない utf-8でエンコードされたデータを含む文字列変数を作成することから始めます。

>>> text = 'á'
>>> text
'\xc3\xa1'
>>> text.decode('utf-8')
u'\xe1'

その上で使用unicode()するとエラーが発生します...

>>> unicode(text)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: 
                    ordinal not in range(128)

...しかし、エンコーディングがわかっている場合は、それを2番目のパラメーターとして使用できます:

>>> unicode(text, 'utf-8')
u'\xe1'
>>> unicode(text, 'utf-8') == text.decode('utf-8')
True

__str__()メソッドでこのテキストを返すクラスがあるとします。

>>> class ReturnsEncoded(object):
...     def __str__(self):
...         return text
... 
>>> r = ReturnsEncoded()
>>> str(r)
'\xc3\xa1'

unicode(r)上記str()と同じエラーが発生するため、使用しているようです。unicode(text)

>>> unicode(r)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: 
                    ordinal not in range(128)

これまでのところ、すべてが計画どおりです!

しかし、誰も予想していなかったように、unicode(r, 'utf-8')試してさえいません:

>>> unicode(r, 'utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: coercing to Unicode: need string or buffer, ReturnsEncoded found

なんで?この一貫性のない動作はなぜですか? バグですか?それは意図されていますか?非常に厄介です。

4

2 に答える 2

8

この振る舞いは紛らわしいように見えますが、意図的なものです。ここでは、 Python 組み込み関数のドキュメント(私がこれを書いているバージョン 2.5.2 用)から Unicode ドキュメント全体を再現します。

unicode([オブジェクト[、エンコーディング[、エラー]]])

次のいずれかのモードを使用して、オブジェクトの Unicode 文字列バージョンを返します。

エンコーディングおよび/またはエラーが指定された場合、unicode() は、エンコーディング用のコーデックを使用して、8 ビット文字列または文字バッファのいずれかであるオブジェクトをデコードします。encoding パラメータは、エンコーディングの名前を与える文字列です。エンコーディングが不明な場合は、LookupError が発生します。エラー処理はエラーに従って行われます。これは、入力エンコーディングで無効な文字の扱いを指定します。エラーが 'strict' (デフォルト) の場合、エラー時に ValueError が送出されますが、値が 'ignore' の場合、エラーは黙って無視され、値が 'replace' の場合、公式の Unicode 置換文字 U+FFFD が発生します。デコードできない入力文字を置き換えるために使用されます。codecsモジュールも参照してください。

オプションのパラメータが指定されていない場合、unicode() は、8 ビット文字列ではなく Unicode 文字列を返すことを除いて、str() の動作を模倣します。より正確には、オブジェクトが Unicode 文字列またはサブクラスである場合、追加のデコードを適用せずにその Unicode 文字列を返します。

__unicode__() メソッドを提供するオブジェクトの場合、引数なしでこのメソッドを呼び出して Unicode 文字列を作成します。他のすべてのオブジェクトについては、8 ビットの文字列バージョンまたは表現が要求され、「strict」モードのデフォルト エンコーディングのコーデックを使用して Unicode 文字列に変換されます。

バージョン 2.0 の新機能。バージョン 2.2 で変更: __unicode__() のサポートが追加されました。

したがって、 を呼び出すとunicode(r, 'utf-8')、最初の引数として 8 ビットの文字列または文字バッファーが必要になるため、メソッドを使用してオブジェクトを強制し、コーデック__str__()を使用してそれをデコードしようとします。utf-8がないとutf-8unicode()関数は__unicode__()オブジェクトのメソッドを探しますが、見つからない__str__()場合は、提案したようにメソッドを呼び出し、デフォルトのコーデックを使用してユニコードに変換しようとします。

于 2008-09-20T01:32:09.070 に答える
5

unicodeテキストのエンコーディングを推測しません。オブジェクト自体を として出力できる場合は、Unicode 文字列を返すメソッドをunicode定義します。__unicode__()


秘密は、unicode(r)実際には__str__()自分自身を呼び出していないことです。__unicode__()代わりに、メソッドを探しています。のデフォルトの実装では、__unicode__()を呼び出し__str__()てから、文字セットを使用してデコードを試みasciiます。エンコーディングを渡すとき、unicode()は最初のオブジェクトがデコード可能なもの、つまりbasestring.


「utf-8」を渡さないと ascii としてデコードしようとするため、動作が奇妙です。しかし、「utf-8」を渡すと、別のエラーが発生します...

これは、「utf-8」を指定すると、最初のパラメーターが文字列のようなオブジェクトとして扱われ、デコードされるためです。それがなければ、パラメータを Unicode に強制されるオブジェクトとして扱います。

私は混乱を理解していません。オブジェクトのtext属性が常に UTF-8 でエンコードされることがわかっている場合は、定義するだけ__unicode__()ですべてが正常に機能します。

于 2008-09-20T00:58:52.303 に答える