sqlite3.ProgrammingError: нельзя использовать 8-битные строки байтов, если вы не используете text_factory, которая может интерпретировать 8-битные строки байтов.

90

Используя SQLite3 в Python, я пытаюсь сохранить сжатую версию фрагмента HTML-кода UTF-8.

Код выглядит так:

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

Если я использую «текст», а не «blob», и не сжимаю фрагмент HTML, он работает нормально (хотя db слишком велик). Когда я использую blob и сжимаю через библиотеку Python zlib, я получаю указанное выше сообщение об ошибке. Я огляделась, но не нашла простого ответа на этот вопрос.

Р. Хилл
источник

Ответы:

94

Если вы хотите использовать 8-битные строки вместо строки Unicode в sqlite3, установите соответствующий text_factory для подключения sqlite:

connection = sqlite3.connect(...)
connection.text_factory = str
заг
источник
7
Это может вызвать проблемы с различными кодировками, поскольку вы все еще пытаетесь анализировать двоичные данные как текст. Вместо этого лучше использовать sqlite3.Binary.
MarioVilas 09
35

Нашел решение, мне следовало потратить еще немного времени на поиски.

Решение состоит в том, чтобы «преобразовать» значение в «буфер» Python, например:

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

Надеюсь, это поможет кому-нибудь другому.

Р. Хилл
источник
1
Когда я это сделал, моя база данных была заполнена текстом base36, что сделало бы базу данных больше, чем при непосредственном хранении большого двоичного объекта.
Брайан Минтон
3
Это неверно, вместо этого вы должны использовать sqlite3.Binary, как указано в документации.
MarioVilas 09
Похоже, что sqlite3.Binary () - это просто псевдоним buffer (), по крайней мере, с github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt
Ага. И также похоже, что этот раздел документации pysqlite действительно поощряет использование buffer (): «Следующие типы Python, таким образом, могут быть отправлены в SQLite без каких-либо проблем: ...» [тип Python] буфер ... [тип 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
Я не думаю, что Python имеет всю схему базы данных в памяти, чтобы обращаться к правильным типам данных - скорее всего, он просто угадывает типы во время выполнения на основе того, что вы ему передаете, поэтому двоичную строку нельзя отличить от текстовой строки.
MarioVilas
Поскольку 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]))
Pranzell
источник
0

Вы можете сохранить значение, используя repr (html) вместо необработанного вывода, а затем использовать eval (html) при получении значения для использования.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html))))
Zwalker
источник
1
Использование eval и repr таким образом очень грязно. Независимо от того, насколько вы доверяете источнику данных.
Джейсон Фрид
Согласен, здесь все лучше, чем eval (). Правильным решением является использование sqlite3.Binary, но если по какой-то причине вы не можете этого сделать, лучше закодировать данные более безопасным способом - например, с помощью base64.
MarioVilas 09