sqlite3.ProgrammingError: no debe utilizar cadenas de bytes de 8 bits a menos que utilice un text_factory que pueda interpretar cadenas de bytes de 8 bits

90

Usando SQLite3 en Python, estoy tratando de almacenar una versión comprimida de un fragmento de código HTML UTF-8.

El código se ve así:

...
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)))

En qué punto aparece 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 utilizo 'texto' en lugar de 'blob' y no comprimo el fragmento de HTML, todo funciona bien (aunque db es demasiado grande). Cuando uso 'blob' y comprimo a través de la biblioteca zlib de Python, obtengo el mensaje de error anterior. Miré a mi alrededor pero no pude encontrar una respuesta simple para esta.

R. Hill
fuente

Respuestas:

94

Si desea utilizar cadenas de 8 bits en lugar de una cadena unicode en sqlite3, establezca aproptiate text_factory para la conexión sqlite:

connection = sqlite3.connect(...)
connection.text_factory = str
zag
fuente
7
Esto puede ocasionarle problemas con diferentes codificaciones, ya que todavía está intentando analizar datos binarios como texto. En su lugar, es mejor usar sqlite3.Binary.
MarioVilas
35

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

La solución es 'lanzar' el valor como un 'búfer' de Python, así:

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

Con suerte, esto ayudará a alguien más.

R. Hill
fuente
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.
Brian Minton
3
Esto es incorrecto, debe usar sqlite3.Binary en su lugar como dice la documentación.
MarioVilas
Parece que sqlite3.Binary () es simplemente un alias de buffer (), al menos a partir de github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt
¿Eh? Y también parece que esta sección de los documentos de pysqlite en realidad fomenta el uso de buffer (): "Los siguientes tipos de Python pueden enviarse a SQLite sin ningún problema: ..." [tipo Python] búfer ... [tipo SQLite] BLOB " docs.python.org/2/library/sqlite3.html#introduction
stevegt
35

Para trabajar con el tipo BLOB, primero debe convertir la cadena comprimida de zlib en datos binarios; de lo contrario, sqlite intentará 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))))
MarioVilas
fuente
esto funciona. Sin embargo, me preguntaba por qué es necesario. ¿El tipo "BLOB" ya indica que los datos de esta columna son binarios? Tenga en cuenta que en Python 2 la cadena puede ser de texto o binaria. ¿No debería sqlite3 simplemente tratar el objeto (cadena comprimida zlib) como binario para el tipo BLOB?
user1783732
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 solo adivine los tipos en tiempo de ejecución en función de lo que le pasa, por lo que una cadena binaria no se puede diferenciar de una cadena de texto.
MarioVilas
Porque SQLite usa el tipo dinámico: sqlite.org/datatype3.html @ user1783732
Lester Cheung
1

Sintaxis:

5 tipos de almacenamiento posibles: NULL, INTEGER, TEXT, REAL y BLOB

BLOB se utiliza generalmente para almacenar modelos en escabeche o modelos en escabeche con eneldo

> cur.execute('''INSERT INTO Tablename(Col1, Col2, Col3, Col4) VALUES(?,?,?,?)''', 
                                      [TextValue, Real_Value, Buffer(model), sqlite3.Binary(model2)])
> conn.commit()

> # Read Data:
> df = pd.read_sql('SELECT * FROM Model, con=conn) 
> model1 = str(df['Col3'].values[0]))
> model2 = str(df['Col'].values[0]))
Pranzell
fuente
0

Puede almacenar el valor usando repr (html) en lugar de la salida sin procesar y luego usar eval (html) cuando recupere el valor para su uso.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html))))
zwalker
fuente
1
Usar eval y repr como este es muy sucio. No importa cuánto confíe en una fuente de datos.
Jason Fried
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