2010-08-06 13 views
78

Al usar SQLite3 en Python, intento almacenar una versión comprimida de un fragmento de código HTML UTF-8.sqlite3.ProgrammingError: no debe usar cadenas de bytes de 8 bits a menos que use text_factory que pueda interpretar cadenas de bytes de 8 bits

código es el siguiente:

... 
c = connection.cursor() 
c.execute('create table blah (cid integer primary key,html blob)') 
... 
c.execute('insert or ignore into blah values (?, ?)',(cid, zlib.compress(html))) 

Momento en el que al llegar el error:

sqlite3.ProgrammingError: You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory = str). It is highly recommended that you instead just switch your application to Unicode strings. 

Si uso 'texto' en lugar de 'burbuja' y no comprima el fragmento de HTML , todo funciona bien (sin embargo, db es grande). Cuando uso 'blob' y comprime a través de la biblioteca zlib de Python, aparece el mensaje de error anterior. Miré a mi alrededor pero no pude encontrar una respuesta simple para esta.

Respuesta

34

Encontré la solución, debería haber pasado un poco más de tiempo buscando.

solución es 'fundido' el valor como 'amortiguador' un Python, así:

c.execute('insert or ignore into blah values (?, ?)',(cid, buffer(zlib.compress(html)))) 

Esperamos que esto ayudará a alguien más.

+11

¿Podría explicarnos por qué funciona esto? – Moshe

+1

Cuando hice esto, mi base de datos estaba llena de texto base36, lo que haría que la base de datos fuera más grande que almacenar el blob directamente. –

+3

Esto es incorrecto, debería usar sqlite3.Binary en su lugar, como dice la documentación. – MarioVilas

81

Si desea utilizar cadenas de 8 bits en vez de cadena Unicode en sqlite3, establecer text_factory approptiate para la conexión SQLite:

connection = sqlite3.connect(...) 
connection.text_factory = str 
+5

Esto puede ocasionar problemas con diferentes codificaciones, ya que todavía está tratando de analizar datos binarios como texto. Lo mejor es usar sqlite3. .Binary en su lugar. – MarioVilas

0

Se puede almacenar el valor con repr (html) en lugar de la salida de crudo y luego use eval (html) al recuperar el valor para usar.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html)))) 
+1

Utilizar eval y repr como este es muy sucio. No importa cuánto confíes en una fuente de datos. –

+0

Estoy de acuerdo, cualquier cosa es mejor que eval() aquí. La solución correcta es usar sqlite3.Binary, pero si no puede por alguna razón, es mejor codificar los datos de una manera más segura, por ejemplo, con base64. – MarioVilas

30

Con el fin de trabajar con el tipo BLOB, primero debe convertir su zlib cadena comprimida en datos binarios - de lo contrario SQLite tratará de procesarla como una cadena de texto. Esto se hace con sqlite3.Binary(). Por ejemplo:

c.execute('insert or ignore into blah values (?, ?)',(cid, 
sqlite3.Binary(zlib.compress(html)))) 
+4

+1. Esta es la respuesta correcta ** actual ** – Yuushi

+0

Esto funciona. Sin embargo, me preguntaba por qué esto es necesario. ¿El tipo "BLOB" alr ¿Ya indica que los datos en esta columna son binarios? Nota en Python 2 la cadena puede ser de texto o binaria. ¿No debería sqlite3 tratar el objeto (cadena comprimida zlib) como binario para el tipo BLOB? – user1783732

+0

No creo que Python tenga todo el esquema de la base de datos en la memoria para consultar los tipos de datos correctos; lo más probable es que adivine los tipos en tiempo de ejecución en función de lo que pase, por lo que una cadena binaria no se puede diferenciar de una cadena de texto . – MarioVilas

Cuestiones relacionadas