sqlite3.ProgrammingError: Ви не повинні використовувати 8-бітові bytestring, якщо ви не використовуєте text_factory, який може інтерпретувати 8-бітні bytestrings


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' та стискаю через бібліотеку zlib Python, я отримую вищезазначене повідомлення про помилку. Я озирнувся, але не зміг знайти просту відповідь на цю.

Відповіді:


94

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

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

7
Це може створити проблеми з різними кодуваннями, оскільки ви все ще намагаєтесь проаналізувати двійкові дані як текст. Краще використовувати sqlite3.Binary замість цього.
MarioVilas

35

Знайшов рішення, я мав би витратити трохи більше часу на пошук.

Рішення полягає в тому, щоб "передати" значення як "буфер" Python, приблизно так:

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

Сподіваємось, це допоможе комусь іншому.


1
Коли я це зробив, моя база даних була повною текстом base36, що зробило б базу даних більшою, ніж безпосереднє зберігання BLOB-масиву.
Брайан Мінтон

3
Це неправильно, замість цього слід використовувати sqlite3.Binary, як сказано в документації.
MarioVilas

Схоже, sqlite3.Binary () - це просто псевдонім buffer (), принаймні як github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt

Га І це також схоже на те, що цей розділ документів pysqlite насправді заохочує використання буфера (): "Таким чином, наступні типи Python можуть бути відправлені до SQLite без будь-яких проблем: ..." [Python type] буфер ... [тип 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.