2013-06-25 8 views
10

私はpickleされたデータを保存するデータベース(mysql)を持っています。ユニコードをpickleしてutf-8データベースに保存する方法

データは、例えば、ユニコードを含む可能性のある辞書であってもよい。

data = {1 : u'é'} 

データベース(mysql)はutf-8です。私はピクルス

import pickle 
pickled_data = pickle.dumps(data) 
print type(pickled_data) # returns <type 'str'> 

結果pickled_dataは文字列です。

これをデータベース(たとえば、テキストフィールド)に保存しようとすると、問題が発生する可能性があります。特に、pickled_dataをデータベースに保存しようとすると、ある時点で、

になっています。これは、pickled_dataが非utf-8文字を持つことができるので意味があります。私の質問は、私はutf-8データベースにpickled_dataをどのように保存するのですか?

私は2つの可能な候補を参照してください。

  1. は、UTF-8にpickle.dumpの結果をエンコードし、それを保存します。私がpickle.loadしたいとき、私はそれを解読しなければならない。

  2. ピクルされた文字列をバイナリ形式(how?)で格納します。これにより、すべての文字がascii内に格納されます。

私の問題は、長期的にこのオプションの1つを選択した結果が何も表示されないことです。変更にはすでに何らかの努力が必要なので、最終的により良い候補者を求めてこの問題に関する意見を求めるように促されます。

(PSこれは、例えばDjangoに有用である)

+0

オプション3:UnicodeデータをUTF-8でエンコードされた文字列として格納します。 –

+0

オプション4:代わりにバイナリ列型を使用します。 –

+2

ピクルデータは*バイナリ*データです。これをUTF-8(テキストエンコーディング)にエンコードすることはできません。 –

答えて

13

あなたはプロトコルバージョン0を使用してもピクルスデータは、不透明な、バイナリデータである:あなたがTextFieldにそれを保存しようとすると

>>> pickle.dumps(data, 0) 
'(dp0\nI1\nV\xe9\np1\ns.' 

をDjangoはそのデータをUTF8にデコードして保存します。これはUTF-8でエンコードされたデータではないため失敗します。それは、代わりにバイナリデータである:

>>> pickled_data.decode('utf8') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/encodings/utf_8.py", line 16, in decode 
    return codecs.utf_8_decode(input, errors, True) 
UnicodeDecodeError: 'utf8' codec can't decode byte 0xe9 in position 9: invalid continuation byte 

ソリューションはTextFieldでこれを格納しようではありません。代わりにBinaryFieldを使用してください。

生のバイナリデータを格納するフィールド。それはbytes割り当てをサポートしています。このフィールドは機能に制限があることに注意してください。たとえば、BinaryField値に対してクエリーセットをフィルタリングすることはできません。

あなたは(Pythonの2列は、Python 3にbytesに改名バイト文字列、です)bytes値を持っています。

データフィールドにデータを格納することを奨励する場合は、明示的にそれをlatin1としてデコードします。ラテン1コーデックマップUnicodeのコードポイントに一対一のバイト:

>>> pickled_data.decode('latin1') 
u'(dp0\nI1\nV\xe9\np1\ns.' 

と再びunpickle化する前に、もう一度エンコードご確認してください。

>>> encoded = pickled_data.decode('latin1') 
>>> pickle.loads(encoded) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/pickle.py", line 1381, in loads 
    file = StringIO(str) 
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 9: ordinal not in range(128) 
>>> pickle.loads(encoded.encode('latin1')) 
{1: u'\xe9'} 

この値を許可すればことに注意してください。ブラウザに戻り、テキストフィールドにもう一度戻ると、ブラウザはそのデータ内の文字を置き換えている可能性があります。たとえば、Internet Explorerは\n文字を\r\nに置き換えます。これは、テキストを処理しているとみなされるためです。

that is a security hole waiting for exploitationのように、ネットワーク接続からのピクルデータの受け入れを許可するべきではありません。

+0

私はdjangoのbinaryFieldが安定するまで待つでしょう。一方、私はTextFieldを使用します。答えをありがとう。 –

+0

私は謝罪します。私は 'BinaryField'がdevのリリースに追加されたばかりであることを認識していませんでした。私はDjangoが以前にバイナリデータフィールドを持っていなかったことに驚いています。 –

+1

@J.C.Leitão:これらのピクルスがネットワークから受け入れたデータである場合は、http://www.zopatista.com/plone/2007/11/09/one-cookie-please/を読んでください。ピックルデータは、信頼できない情報源から受け入れられることは決してありません**。 –

関連する問題