sqlite3.ProgrammingError: Sie dürfen keine 8-Bit-Bytestrings verwenden, es sei denn, Sie verwenden eine text_factory, die 8-Bit-Bytestrings interpretieren kann

90

Mit SQLite3 in Python versuche ich, eine komprimierte Version eines Ausschnitts aus UTF-8-HTML-Code zu speichern.

Code sieht so aus:

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

An welcher Stelle erhalten Sie den Fehler:

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.

Wenn ich 'Text' anstelle von 'Blob' verwende und das HTML-Snippet nicht komprimiere, funktioniert alles einwandfrei (db ist jedoch zu groß). Wenn ich 'blob' verwende und über die Python zlib-Bibliothek komprimiere, wird die obige Fehlermeldung angezeigt. Ich sah mich um, konnte aber keine einfache Antwort auf diese Frage finden.

R. Hill
quelle

Antworten:

93

Wenn Sie in sqlite3 8-Bit-Zeichenfolgen anstelle von Unicode-Zeichenfolgen verwenden möchten, legen Sie die entsprechende text_factory für die SQLite-Verbindung fest:

connection = sqlite3.connect(...)
connection.text_factory = str
Zack
quelle
7
Dies kann zu Problemen mit verschiedenen Codierungen führen, da Sie immer noch versuchen, Binärdaten als Text zu analysieren. Verwenden Sie stattdessen am besten sqlite3.Binary.
MarioVilas
35

Wenn ich die Lösung gefunden habe, hätte ich etwas mehr Zeit mit der Suche verbringen sollen.

Die Lösung besteht darin, den Wert wie folgt in einen Python-Puffer umzuwandeln:

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

Hoffentlich hilft das jemand anderem.

R. Hill
quelle
1
Als ich dies tat, war meine Datenbank voll mit Base36-Text, wodurch die Datenbank größer wurde als das direkte Speichern des Blobs.
Brian Minton
3
Dies ist falsch. Verwenden Sie stattdessen sqlite3.Binary, wie in der Dokumentation angegeben.
MarioVilas
Es sieht so aus, als ob sqlite3.Binary () einfach ein Alias ​​von buffer () ist, zumindest ab github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt
Huh. Und es sieht auch so aus, als würde dieser Abschnitt der pysqlite-Dokumente tatsächlich die Verwendung von buffer () fördern: "Die folgenden Python-Typen können somit problemlos an SQLite gesendet werden: ..." [Python-Typ] buffer ... [SQLite-Typ] BLOB " docs.python.org/2/library/sqlite3.html#introduction
stevegt
35

Um mit dem BLOB-Typ arbeiten zu können, müssen Sie zuerst Ihre komprimierte zlib-Zeichenfolge in Binärdaten konvertieren. Andernfalls versucht sqlite, sie als Textzeichenfolge zu verarbeiten. Dies geschieht mit sqlite3.Binary (). Beispielsweise:

c.execute('insert or ignore into blah values (?, ?)',(cid, 
sqlite3.Binary(zlib.compress(html))))
MarioVilas
quelle
das funktioniert. Ich habe mich jedoch gefragt, warum dies erforderlich ist. Hat der Typ "BLOB" bereits angegeben, dass die Daten in dieser Spalte binär sind? Beachten Sie, dass die Zeichenfolge in Python 2 entweder Text oder Binär sein kann. Sollte sqlite3 das Objekt (zlib-komprimierte Zeichenfolge) nicht einfach als binär für den BLOB-Typ behandeln?
user1783732
Ich glaube nicht, dass Python das gesamte Datenbankschema im Speicher hat, um die richtigen Datentypen zu konsultieren - höchstwahrscheinlich werden die Typen zur Laufzeit nur anhand der Übergabe erraten, sodass eine Binärzeichenfolge nicht von einer Textzeichenfolge unterschieden werden kann.
MarioVilas
Weil SQLite den dynamischen Typ verwendet: sqlite.org/datatype3.html @ user1783732
Lester Cheung
1

Syntax:

5 Arten möglicher Speicher: NULL, INTEGER, TEXT, REAL und BLOB

BLOB wird im Allgemeinen verwendet, um eingelegte Modelle oder eingelegte Dillmodelle zu lagern

> 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
quelle
0

Sie können den Wert mit repr (html) anstelle der Rohausgabe speichern und dann eval (html) verwenden, um den zu verwendenden Wert abzurufen.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html))))
zwalker
quelle
1
Die Verwendung von eval und repr wie diesem ist sehr schmutzig. Egal wie sehr Sie einer Datenquelle vertrauen.
Jason Fried
Ich stimme zu, hier ist alles besser als eval (). Die richtige Lösung ist die Verwendung von sqlite3.Binary. Wenn Sie dies jedoch aus irgendeinem Grund nicht können, ist es besser, die Daten sicherer zu codieren - beispielsweise mit base64.
MarioVilas