sqlite3.ProgrammingError: Nie możesz używać 8-bitowych bajtów, chyba że używasz text_factory, który może interpretować 8-bitowe bajty


90

Używając SQLite3 w Pythonie, próbuję przechowywać skompresowaną wersję fragmentu kodu HTML UTF-8.

Kod wygląda następująco:

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

W którym momencie pojawi się błąd:

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.

Jeśli użyję „tekstu” zamiast „bloba” i nie kompresuję fragmentu kodu HTML, wszystko działa dobrze (choć db jest za duży). Kiedy używam „blob” i kompresuję przez bibliotekę Python zlib, pojawia się powyższy komunikat o błędzie. Rozejrzałem się, ale nie mogłem znaleźć prostej odpowiedzi na to pytanie.

Odpowiedzi:


94

Jeśli chcesz używać 8-bitowych ciągów zamiast znaków Unicode w sqlite3, ustaw odpowiednią wartość text_factory dla połączenia sqlite:

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

7
Może to powodować problemy z różnymi kodowaniami, ponieważ nadal próbujesz analizować dane binarne jako tekst. Najlepiej zamiast tego użyć sqlite3.Binary.
— MarioVilas

35

Znalazłem rozwiązanie, powinienem był poświęcić trochę więcej czasu na szukanie.

Rozwiązaniem jest „rzutowanie” wartości jako „bufor” Pythona, na przykład:

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

Mam nadzieję, że pomoże to komuś innemu.


1
Kiedy to zrobiłem, moja baza danych była pełna tekstu base36, co spowodowałoby, że baza danych byłaby większa niż bezpośrednie przechowywanie obiektu blob.
— Brian Minton,

3
To jest niepoprawne, powinieneś zamiast tego użyć sqlite3.Binary, jak mówi dokumentacja.
— MarioVilas

Wygląda na to, że sqlite3.Binary () to po prostu alias funkcji buffer (), przynajmniej od github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
— stevegt

Huh. Wygląda też na to, że ta sekcja dokumentacji pysqlite faktycznie zachęca do użycia funkcji buffer (): „Następujące typy Pythona można więc bez problemu wysłać do SQLite: ...” [typ Pythona] bufor ... [typ SQLite] BLOB " docs.python.org/2/library/sqlite3.html#introduction
— stevegt

35

Aby pracować z typem BLOB, musisz najpierw przekonwertować skompresowany ciąg zlib na dane binarne - w przeciwnym razie sqlite spróbuje przetworzyć go jako ciąg tekstowy. Odbywa się to za pomocą sqlite3.Binary (). Na przykład:

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

to działa. Jednak zastanawiałem się, dlaczego jest to potrzebne. Czy typ „BLOB” już wskazuje, że dane w tej kolumnie są binarne? Uwaga w Pythonie 2 ciąg może być tekstowy lub binarny. Czy sqlite3 nie powinien po prostu traktować obiektu (skompresowany ciąg zlib) jako binarny dla typu BLOB?
— user1783732

Nie sądzę, że Python ma w pamięci cały schemat bazy danych, aby sprawdzić poprawne typy danych - najprawdopodobniej po prostu zgaduje typy w czasie wykonywania na podstawie tego, co go przekazujesz, więc ciąg binarny nie może być odróżniony od ciągu tekstowego.
— MarioVilas,

Ponieważ SQLite używa typu dynamicznego: sqlite.org/datatype3.html @ user1783732
— Lester Cheung

1

Składnia:

5 możliwych typów przechowywania: NULL, INTEGER, TEXT, REAL i BLOB

BLOB jest zwykle używany do przechowywania modeli marynowanych lub modeli marynowanych z koperkiem

> 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

Możesz zapisać wartość za pomocą repr (html) zamiast surowych danych wyjściowych, a następnie użyć eval (html) podczas pobierania wartości do użycia.

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

1
Używanie eval i repr w ten sposób jest bardzo brudne. Bez względu na to, jak bardzo ufasz źródłu danych.
— Jason Fried

Zgadzam się, wszystko jest lepsze niż eval () tutaj. Właściwym rozwiązaniem jest użycie sqlite3.Binary, ale jeśli z jakiegoś powodu nie możesz, lepiej zakodować dane w bezpieczniejszy sposób - na przykład za pomocą base64.
— MarioVilas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.