sqlite3.ProgrammingError: non è necessario utilizzare bytestrings a 8 bit a meno che non si utilizzi una text_factory in grado di interpretare bytestrings a 8 bit


90

Utilizzando SQLite3 in Python, sto cercando di memorizzare una versione compressa di uno snippet di codice HTML UTF-8.

Il codice ha questo aspetto:

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

A quel punto a ottenere l'errore:

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.

Se uso "text" invece di "blob" e non comprimo lo snippet HTML, funziona perfettamente (db è troppo grande). Quando uso "blob" e comprimo tramite la libreria zlib di Python, ricevo il messaggio di errore precedente. Mi sono guardato intorno ma non sono riuscito a trovare una risposta semplice per questo.

Risposte:


94

Se si desidera utilizzare stringhe a 8 bit invece della stringa Unicode in sqlite3, impostare text_factory appropriato per la connessione sqlite:

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

7
Ciò potrebbe darti problemi con codifiche diverse, poiché stai ancora cercando di analizzare i dati binari come testo. È meglio usare invece sqlite3.Binary.
MarioVilas

35

Trovata la soluzione, avrei dovuto dedicare un po 'più di tempo alla ricerca.

La soluzione è 'lanciare' il valore come un 'buffer' Python, in questo modo:

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

Si spera che questo aiuti qualcun altro.


1
Quando l'ho fatto, il mio database era pieno di testo base36, il che renderebbe il database più grande rispetto all'archiviazione diretta del BLOB.
Brian Minton

3
Questo non è corretto, dovresti usare sqlite3.Binary invece come dice la documentazione.
MarioVilas

Sembra che sqlite3.Binary () sia semplicemente un alias di buffer (), almeno a partire da github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt

Eh. E sembra anche che questa sezione dei documenti pysqlite incoraggia effettivamente l'uso di buffer (): "I seguenti tipi di Python possono quindi essere inviati a SQLite senza alcun problema: ..." [Tipo Python] buffer ... [Tipo SQLite] BLOB " docs.python.org/2/library/sqlite3.html#introduction
stevegt

35

Per lavorare con il tipo BLOB, devi prima convertire la tua stringa compressa zlib in dati binari, altrimenti sqlite proverà a elaborarla come stringa di testo. Questo viene fatto con sqlite3.Binary (). Per esempio:

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

questo funziona. Tuttavia, mi chiedevo perché è necessario. Il tipo "BLOB" indicava già che i dati in questa colonna sono binari? Nota in Python 2 la stringa può essere testo o binaria. Sqlite3 non dovrebbe semplicemente trattare l'oggetto (stringa compressa zlib) come binario per il tipo BLOB?
user1783732

Non penso che Python abbia l'intero schema del database in memoria per consultare i tipi di dati corretti - molto probabilmente indovina solo i tipi in runtime in base a ciò che viene passato, quindi una stringa binaria non può essere differenziata da una stringa di testo.
MarioVilas

Perché SQLite utilizza il tipo dinamico: sqlite.org/datatype3.html @ user1783732
Lester Cheung

1

Sintassi:

5 tipi di memorizzazione possibili: NULL, INTEGER, TEXT, REAL e BLOB

BLOB viene generalmente utilizzato per conservare modelli in salamoia o modelli in salamoia

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

0

È possibile memorizzare il valore utilizzando repr (html) invece dell'output non elaborato e quindi utilizzare eval (html) quando si recupera il valore per l'uso.

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

1
Usare eval e repr in questo modo è molto sporco. Non importa quanto ti fidi di una fonte di dati.
Jason Fried

Sono d'accordo, tutto è meglio di eval () qui. La soluzione giusta è usare sqlite3.Binary, ma se non puoi per qualche motivo, è meglio codificare i dati in un modo più sicuro, ad esempio con base64.
MarioVilas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.