sqlite3.ProgrammingError: คุณต้องไม่ใช้ 8-bit bytestrings เว้นแต่คุณจะใช้ text_factory ที่สามารถตีความ 8-bit bytestrings


90

ใช้ SQLite3 ใน Python ฉันพยายามจัดเก็บข้อมูลโค้ด UTF-8 HTML เวอร์ชันบีบอัด

รหัสมีลักษณะดังนี้:

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

ถ้าฉันใช้ 'text' แทนที่จะเป็น 'blob' และไม่บีบอัดข้อมูลโค้ด HTML มันก็ใช้ได้ดี (db จะใหญ่) เมื่อฉันใช้ 'blob' และบีบอัดผ่านไลบรารี Python zlib ฉันได้รับข้อความแสดงข้อผิดพลาดข้างต้น ฉันมองไปรอบ ๆ แต่ไม่พบคำตอบง่ายๆสำหรับข้อนี้

คำตอบ:


94

หากคุณต้องการใช้สตริง 8 บิตแทนสตริง Unicode ใน sqlite3 ให้ตั้งค่าการอนุมัติ text_factory สำหรับการเชื่อมต่อ sqlite:

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 ซึ่งจะทำให้ฐานข้อมูลมีขนาดใหญ่กว่าการจัดเก็บหยดโดยตรง
Brian Minton

3
ไม่ถูกต้องคุณควรใช้ sqlite3.Binary แทนตามที่เอกสารระบุ
MarioVilas

ดูเหมือน sqlite3.Binary () เป็นเพียงนามแฝงของ buffer () อย่างน้อยก็เป็นgithub.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt

ฮะ. และดูเหมือนว่าส่วนนี้ของเอกสาร pysqlite จะสนับสนุนการใช้งานบัฟเฟอร์ () จริง ๆ : "ประเภท 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 แบบนี้ถือว่าสกปรกมาก ไม่ว่าคุณจะเชื่อถือแหล่งข้อมูลมากแค่ไหนก็ตาม
Jason Fried

ฉันเห็นด้วยมีอะไรดีไปกว่า eval () ที่นี่ วิธีแก้ปัญหาที่เหมาะสมคือการใช้ sqlite3.Binary แต่ถ้าคุณทำไม่ได้ด้วยเหตุผลบางประการควรเข้ารหัสข้อมูลด้วยวิธีที่ปลอดภัยกว่าเช่น base64
MarioVilas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.