sqlite3.ProgrammingError: Vous ne devez pas utiliser de chaînes d'octets 8 bits sauf si vous utilisez une usine de texte qui peut interpréter des chaînes d'octets 8 bits

90

En utilisant SQLite3 en Python, j'essaie de stocker une version compressée d'un extrait de code HTML UTF-8.

Le code ressemble à ceci:

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

À quel moment obtenir l'erreur:

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 j'utilise «text» plutôt que «blob» et que je ne compresse pas l'extrait de code HTML, cela fonctionne très bien (la base de données est trop grande cependant). Lorsque j'utilise 'blob' et que je compresse via la bibliothèque Python zlib, j'obtiens le message d'erreur ci-dessus. J'ai regardé autour de moi mais je n'ai pas trouvé de réponse simple pour celle-ci.

R. Hill
la source

Réponses:

94

Si vous souhaitez utiliser des chaînes 8 bits au lieu d'une chaîne Unicode dans sqlite3, définissez approptiate text_factory pour la connexion sqlite:

connection = sqlite3.connect(...)
connection.text_factory = str
zag
la source
7
Cela peut vous poser des problèmes avec différents encodages, car vous essayez toujours d'analyser des données binaires sous forme de texte. Il est préférable d'utiliser sqlite3.Binary à la place.
MarioVilas
35

J'ai trouvé la solution, j'aurais dû passer un peu plus de temps à chercher.

La solution consiste à `` convertir '' la valeur en `` tampon '' Python, comme ceci:

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

J'espère que cela aidera quelqu'un d'autre.

R. Hill
la source
1
Quand j'ai fait cela, ma base de données était pleine de texte base36, ce qui rendrait la base de données plus grande que le stockage direct du blob.
Brian Minton
3
Ceci est incorrect, vous devriez utiliser sqlite3.Binary à la place comme le dit la documentation.
MarioVilas
Cela ressemble à sqlite3.Binary () est simplement un alias de buffer (), du moins à partir de github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt
Huh. Et il semble aussi que cette section de la documentation pysqlite encourage en fait l'utilisation de buffer (): "Les types Python suivants peuvent ainsi être envoyés à SQLite sans aucun problème: ..." [type Python] buffer ... [type SQLite] BLOB " docs.python.org/2/library/sqlite3.html#introduction
stevegt
35

Afin de travailler avec le type BLOB, vous devez d'abord convertir votre chaîne compressée zlib en données binaires - sinon sqlite essaiera de la traiter comme une chaîne de texte. Ceci est fait avec sqlite3.Binary (). Par exemple:

c.execute('insert or ignore into blah values (?, ?)',(cid, 
sqlite3.Binary(zlib.compress(html))))
MarioVilas
la source
cela marche. Cependant, je me demandais pourquoi cela était nécessaire. Le type «BLOB» indique-t-il déjà que les données de cette colonne sont binaires? Notez qu'en Python 2, la chaîne peut être du texte ou du binaire. Sqlite3 ne devrait-il pas simplement traiter l'objet (chaîne compressée zlib) comme binaire pour le type BLOB?
user1783732
Je ne pense pas que Python ait tout le schéma de base de données en mémoire pour consulter les types de données corrects - très probablement, il devine simplement les types à l'exécution en fonction de ce que vous lui transmettez, donc une chaîne binaire ne peut pas être différenciée d'une chaîne de texte.
MarioVilas
Parce que SQLite utilise un type dynamique: sqlite.org/datatype3.html @ user1783732
Lester Cheung
1

Syntaxe:

5 types de stockage possibles: NULL, INTEGER, TEXT, REAL et BLOB

BLOB est généralement utilisé pour stocker des modèles marinés ou des modèles marinés à l'aneth

> 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
la source
0

Vous pouvez stocker la valeur en utilisant repr (html) au lieu de la sortie brute, puis utiliser eval (html) lors de la récupération de la valeur à utiliser.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html))))
zwalker
la source
1
Utiliser eval et repr comme ça est très sale. Peu importe à quel point vous faites confiance à une source de données.
Jason Fried
Je suis d'accord, tout est meilleur que eval () ici. La bonne solution consiste à utiliser sqlite3.Binary, mais si vous ne pouvez pas pour une raison quelconque, il est préférable d'encoder les données de manière plus sûre - par exemple avec base64.
MarioVilas