sqlite3.ProgrammingError: Bạn không được sử dụng bytestrings 8 bit trừ khi bạn sử dụng text_factory có thể giải thích bytestrings 8 bit


90

Sử dụng SQLite3 bằng Python, tôi đang cố gắng lưu trữ phiên bản nén của một đoạn mã HTML UTF-8.

Mã trông như thế này:

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

Tại thời điểm nhận được lỗi:

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.

Nếu tôi sử dụng 'văn bản' thay vì 'đốm màu' và không nén đoạn mã HTML, nó hoạt động tốt (mặc dù db là lớn). Khi tôi sử dụng 'blob' và nén qua thư viện Python zlib, tôi nhận được thông báo lỗi ở trên. Tôi nhìn xung quanh nhưng không thể tìm thấy câu trả lời đơn giản cho câu trả lời này.

Câu trả lời:


93

Nếu bạn muốn sử dụng chuỗi 8 bit thay vì chuỗi unicode trong sqlite3, hãy đặt text_factory phê duyệt cho kết nối sqlite:

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

7
Điều này có thể khiến bạn gặp sự cố với các bảng mã khác nhau, vì bạn vẫn đang cố gắng phân tích cú pháp dữ liệu nhị phân dưới dạng văn bản. Tốt nhất là sử dụng sqlite3.Binary để thay thế.
MarioVilas

35

Đã tìm ra giải pháp, đáng lẽ tôi phải dành thêm một chút thời gian để tìm kiếm.

Giải pháp là 'ép' giá trị dưới dạng 'bộ đệm' Python, như sau:

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

Hy vọng rằng điều này sẽ giúp ai đó khác.


1
Khi tôi làm điều này, cơ sở dữ liệu của tôi chứa đầy văn bản base36, điều này sẽ làm cho cơ sở dữ liệu lớn hơn so với việc lưu trữ trực tiếp đốm màu.
Brian Minton

3
Điều này không chính xác, bạn nên sử dụng sqlite3.Binary thay thế như tài liệu hướng dẫn.
MarioVilas

Có vẻ như sqlite3.Binary () chỉ đơn giản là một bí danh của buffer (), ít nhất là kể từ github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt

Huh. Và có vẻ như phần này của tài liệu pysqlite thực sự khuyến khích việc sử dụng buffer (): "Do đó, các loại Python sau có thể được gửi đến SQLite mà không gặp bất kỳ vấn đề gì: ..." [Kiểu Python] bộ đệm ... [Kiểu SQLite] BLOB" docs.python.org/2/library/sqlite3.html#introduction
stevegt

35

Để làm việc với kiểu BLOB, trước tiên bạn phải chuyển đổi chuỗi nén zlib của mình thành dữ liệu nhị phân - nếu không sqlite sẽ cố gắng xử lý nó dưới dạng chuỗi văn bản. Điều này được thực hiện với sqlite3.Binary (). Ví dụ:

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

những công việc này. Tuy nhiên, tôi đã tự hỏi tại sao điều này là cần thiết. Loại "BLOB" đã chỉ ra dữ liệu trong cột này là nhị phân chưa? Lưu ý trong Python 2, chuỗi có thể là văn bản hoặc nhị phân. Không nên sqlite3 chỉ coi đối tượng (chuỗi nén zlib) là nhị phân cho kiểu BLOB?
user1783732

Tôi không nghĩ rằng Python có toàn bộ lược đồ cơ sở dữ liệu trong bộ nhớ để tham khảo các kiểu dữ liệu chính xác - rất có thể nó chỉ đoán các kiểu trong thời gian chạy dựa trên những gì bạn chuyển nó, vì vậy không thể phân biệt chuỗi nhị phân với chuỗi văn bản.
MarioVilas

Vì SQLite sử dụng kiểu động: sqlite.org/datatype3.html @ user1783732
Lester Cheung

1

Cú pháp:

5 loại lưu trữ có thể có: NULL, INTEGER, TEXT, REAL và BLOB

BLOB thường được sử dụng để lưu trữ các mô hình ngâm hoặc các mô hình ngâm thì là

> 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

Bạn có thể lưu trữ giá trị bằng cách sử dụng repr (html) thay vì đầu ra thô và sau đó sử dụng eval (html) khi truy xuất giá trị để sử dụng.

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

1
Sử dụng eval và repr như thế này là rất bẩn. Bất kể bạn tin tưởng nguồn dữ liệu đến mức nào.
Jason Fried

Tôi đồng ý, bất cứ điều gì tốt hơn so với eval () ở đây. Giải pháp phù hợp là sử dụng sqlite3.Binary, nhưng nếu bạn không thể vì một lý do nào đó, tốt hơn là nên mã hóa dữ liệu theo cách an toàn hơn - ví dụ với base64.
MarioVilas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.