2013-06-25 259 views
10

我有一个数据库(mysql),我想存储pickled数据。如何pickle unicodes并将它们保存在utf-8数据库中

该数据可以是例如字典,其可以包含unicode,例如,

data = {1 : u'é'} 

和数据库(mysql)是在utf-8中。

当我味酸,

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

所得pickled_data是一个字符串。

当我尝试将其存储在数据库中时(例如在文本框中),这可能会导致问题。特别是,我得到了一点

UnicodeDecodeError "'utf8' codec can't decode byte 0xe9 in position X" 

当试图保存pickled_data在数据库中。这很有意义,因为pickled_data可以有非UTF-8字符。我的问题是如何将pickled_data存储在utf-8数据库中?

我看到了两个可能的候选人:

  1. 编码和pickle.dump为UTF-8的结果和存储。当我想pickle.load时,我必须解码它。

  2. 以二进制格式存储腌制字符串(如何?),强制所有字符在ascii中。

我的问题是,我没有看到从长远来看选择这个选项之一后果是什么。由于改变已经需要一些努力,所以我被迫就这个问题征求意见,要求最终更好的候选人。

(PS例如,这是有用的Django

+0

选项3:将unicode数据存储为UTF-8编码的字符串。 –

+0

选项4:改为使用二进制列类型。 –

+2

Pickle数据是* binary *数据。您无法将其编码为UTF-8(文本编码)。 –

回答

13

味酸数据是不透明的,二进制数据,即使您使用协议版本0:

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

当您尝试存储在TextField ,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值上的查询集。

你有一个bytes值(Python的2串是字节字符串,在Python 3更名为bytes)。

如果你坚持存储在文本字段中的数据,明确它作为解码latin1;拉丁1编解码器的地图字节单对一个Unicode代码点:

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

,并确保你编码一遍又取储存前:

>>> 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/; pickle数据应该**从未被**从不可信的来源接受。 –

相关问题