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

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 09
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.