sqlite3.ProgrammingError:不得使用8位字节串,除非您使用可以解释8位字节串的text_factory


90

我正在Python中使用SQLite3,试图存储UTF-8 HTML代码段的压缩版本。

代码如下:

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

此时出现错误:

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.

如果我使用'text'而不是'blob'并且不压缩HTML代码段,则可以正常工作(尽管db很大)。当我使用“ blob”并通过Python zlib库进行压缩时,出现上述错误消息。我环顾四周,但找不到一个简单的答案。

Answers:


94

如果要在sqlite3中使用8位字符串而不是unicode字符串,请为sqlite连接设置approptiate text_factory:

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

7
这可能会给您带来不同编码的问题,因为您仍在尝试将二进制数据解析为文本。最好改用sqlite3.Binary。
MarioVilas 2014年

35

找到解决方案后,我应该花更多的时间进行搜索。

解决方案是将值“转换”为Python“缓冲区”,如下所示:

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

希望这会对其他人有所帮助。


1
当我这样做时,我的数据库充满了base36文本,这将使数据库比直接存储blob大。
Brian Minton 2014年

3
这是不正确的,您应该使用sqlite3.Binary,如文档所述。
MarioVilas 2014年

看起来sqlite3.Binary()只是buffer()的别名,至少从github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54开始
stevegt

嗯 而且看起来pysqlite文档的这一部分实际上鼓励使用buffer():“因此,以下Python类型可以毫无问题地发送到SQLite:...” [Python类型] buffer ... [SQLite类型] BLOB” docs.python.org/2/library/sqlite3.html#introduction
stevegt

35

为了使用BLOB类型,必须首先将zlib压缩的字符串转换为二进制数据-否则sqlite会尝试将其作为文本字符串处理。这是通过sqlite3.Binary()完成的。例如:

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

这可行。但是,我想知道为什么需要这样做。类型“ BLOB”是否已经表明该列中的数据是二进制的?请注意,在Python 2中,字符串可以是文本或二进制。sqlite3是否不应该将对象(zlib压缩字符串)视为BLOB类型的二进制文件?
user1783732 2014年

我不认为Python在内存中拥有完整的数据库架构来查询正确的数据类型-最有可能的是,它只是根据传递的内容猜测运行时的类型,因此二进制字符串不能与文本字符串区分开。
MarioVilas 2014年

由于SQLite使用动态类型,因此:sqlite.org/datatype3.html @ user1783732
Lester Cheung

1

句法:

5种可能的存储类型:NULL,INTEGER,TEXT,REAL和BLOB

BLOB通常用于存储腌制模型或莳萝腌制模型

> 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

您可以使用repr(html)代替原始输出来存储值,然后在检索要使用的值时使用eval(html)。

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

1
像这样使用eval和repr非常脏。无论您多么信任数据源。
杰森·弗里德

我同意,这里有什么要比eval()好。正确的解决方案是使用sqlite3.Binary,但如果由于某些原因不能这样做,最好以更安全的方式对数据进行编码-例如使用base64。
MarioVilas 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.