sqlite3.ProgrammingError: Anda tidak boleh menggunakan bytestring 8-bit kecuali Anda menggunakan text_factory yang dapat menafsirkan bytestring 8-bit

90

Menggunakan SQLite3 dengan Python, saya mencoba menyimpan versi terkompresi dari potongan kode UTF-8 HTML.

Kode terlihat seperti ini:

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

Di mana titik mendapatkan kesalahan:

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.

Jika saya menggunakan 'teks' daripada 'gumpalan' dan tidak mengompres potongan HTML, itu berfungsi dengan baik (meskipun db terlalu besar). Ketika saya menggunakan 'blob' dan kompres melalui pustaka Python zlib, saya mendapatkan pesan kesalahan di atas. Saya melihat sekeliling tetapi tidak dapat menemukan jawaban sederhana untuk yang satu ini.

R. Hill
sumber

Jawaban:

94

Jika Anda ingin menggunakan string 8-bit daripada string unicode di sqlite3, setel text_factory yang sesuai untuk koneksi sqlite:

connection = sqlite3.connect(...)
connection.text_factory = str
zag
sumber
7
Ini dapat menimbulkan masalah dengan encoding yang berbeda, karena Anda masih mencoba mengurai data biner sebagai teks. Sebaiknya gunakan sqlite3.Binary sebagai gantinya.
MarioVilas
35

Menemukan solusinya, saya seharusnya menghabiskan lebih banyak waktu untuk mencari.

Solusinya adalah 'memasukkan' nilai sebagai 'buffer' Python, seperti ini:

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

Semoga ini bisa membantu orang lain.

R. Hill
sumber
1
Ketika saya melakukan ini, database saya penuh dengan teks base36, yang akan membuat database lebih besar daripada menyimpan blob secara langsung.
Brian Minton
3
Ini tidak benar, Anda harus menggunakan sqlite3.Binary sebagai gantinya.
MarioVilas
Sepertinya sqlite3.Binary () hanyalah alias dari buffer (), setidaknya pada github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt
Hah. Dan sepertinya bagian dari dokumen pysqlite ini benar-benar mendorong penggunaan buffer (): "Dengan demikian, tipe Python berikut dapat dikirim ke SQLite tanpa masalah: ..." [Python type] buffer ... [SQLite type] BLOB " docs.python.org/2/library/sqlite3.html#introduction
stevegt
35

Untuk bekerja dengan tipe BLOB, Anda harus terlebih dahulu mengubah string terkompresi zlib Anda menjadi data biner - jika tidak, sqlite akan mencoba memprosesnya sebagai string teks. Ini dilakukan dengan sqlite3.Binary (). Sebagai contoh:

c.execute('insert or ignore into blah values (?, ?)',(cid, 
sqlite3.Binary(zlib.compress(html))))
MarioVilas
sumber
ini bekerja. Namun, saya bertanya-tanya mengapa ini diperlukan. Apakah jenis "BLOB" sudah menunjukkan bahwa data di kolom ini biner? Catatan di Python 2 string bisa berupa teks atau biner. Bukankah sqlite3 seharusnya hanya memperlakukan objek (string terkompresi zlib) sebagai biner untuk tipe BLOB?
pengguna1783732
Saya tidak berpikir Python memiliki seluruh skema database dalam memori untuk berkonsultasi dengan tipe data yang benar - kemungkinan besar itu hanya menebak jenis pada runtime berdasarkan apa yang Anda berikan, jadi string biner tidak dapat dibedakan dari string teks.
MarioVilas
Karena SQLite menggunakan tipe dinamis: sqlite.org/datatype3.html @ user1783732
Lester Cheung
1

Sintaksis:

5 jenis penyimpanan yang memungkinkan: NULL, INTEGER, TEXT, REAL dan BLOB

BLOB umumnya digunakan untuk menyimpan model acar atau model acar dill

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

Anda bisa menyimpan nilai menggunakan repr (html) sebagai ganti keluaran mentah dan kemudian menggunakan eval (html) saat mengambil nilai untuk digunakan.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html))))
zwalker
sumber
1
Menggunakan eval dan repr seperti ini sangat kotor. Tidak peduli seberapa besar Anda mempercayai sumber data.
Jason Fried
Saya setuju, di sini ada yang lebih baik daripada eval (). Solusi yang tepat adalah menggunakan sqlite3.Binary, tetapi jika Anda tidak bisa karena alasan tertentu, lebih baik menyandikan data dengan cara yang lebih aman - misalnya dengan base64.
MarioVilas