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.