2010-03-06 10 views
64

Zacząłem od próby do przechowywania ciągów w SQLite przy użyciu Python i dostał wiadomość:SQLite, Python, Unicode i UTF-non dane

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.

Ok, przeszedłem do ciągów Unicode. Następnie zacząłem otrzymywać komunikat:

sqlite3.OperationalError: Could not decode to UTF-8 column 'tag_artist' with text 'Sigur Rós'

podczas próby pobrania danych z bazy danych. Badania naukowe i zacząłem go w utf8 kodowania, ale zaczyna Then „Sigur Ros” wyglądać jak „Sigur Rós”

uwaga: Moja konsola została powołana do wyświetlenia w „latin_1” jako @John Machin zauważył.

Co daje? Po przeczytaniu this, opisując dokładnie tę samą sytuację, w której się znajduję, wydaje mi się, że poradą jest zignorowanie drugiej porady i użycie w końcu 8-bitowych bytów.

Nie wiedziałem zbyt wiele o unicode i utf zanim rozpocząłem ten proces. Dowiedziałem się całkiem sporo w ciągu ostatnich kilku godzin, ale nadal nie wiem, czy istnieje sposób, aby poprawnie przekonwertować "ó" z latin-1 na utf-8 i nie manipulować nim. Jeśli tak nie jest, dlaczego sqlite "bardzo polecam" Przełączam swoją aplikację na ciągi Unicode?


zamierzam aktualizować to pytanie z podsumowaniem i jakiś przykład kodu wszystkiego nauczyłem się w ciągu ostatnich 24 godzin tak, że ktoś w moich butach mogą mieć łatwy (ER) przewodnika. Jeśli informacje, które zamieszczam, są błędne lub wprowadzające w błąd w jakikolwiek sposób, proszę powiedz mi, a zaktualizuję, lub jeden z was, starsi faceci, może zaktualizować.


Streszczenie odpowiedzi

Pozwól pierwszym państwie cel jak rozumiem. Celem przetwarzania różnych kodowań, jeśli próbujesz dokonać konwersji między nimi, jest zrozumienie kodowania źródłowego, a następnie przekonwertowanie go na kod Unicode za pomocą tego kodowania źródłowego, a następnie przekonwertowanie go na pożądane kodowanie. Unicode jest podstawą, a kodowanie to odwzorowanie podzbiorów tej bazy. utf_8 ma miejsce na każdy znak w unicode, ale ponieważ nie są one w tym samym miejscu co na przykład latin_1, łańcuch zakodowany w utf_8 i wysłany na konsolę latin_1 nie będzie wyglądał tak, jak oczekujesz. W python proces dotarcia do Unicode i do innego kodowania wygląda następująco:

str.decode('source_encoding').encode('desired_encoding') 

czy STR jest już unicode

str.encode('desired_encoding') 

SQLite I faktycznie nie chcemy zakodować go ponownie, Chciałem go rozszyfrować i pozostawić w formacie Unicode. Oto cztery rzeczy, o których możesz być świadomy podczas próby pracy z unikodem i kodowaniem w pythonie.

  1. Kodowanie napisu, z którym chcesz pracować, oraz kodowanie, do którego chcesz go uzyskać.
  2. Kodowanie systemu.
  3. Kodowanie konsoli.
  4. kodowanie pliku źródłowego

opracowania:

(1) Kiedy czytasz ciąg ze źródła, to musi mieć jakieś kodowanie, jak latin_1 lub UTF_8. W moim przypadku otrzymuję ciągi znaków z nazw plików, więc niestety, mógłbym uzyskać jakiekolwiek kodowanie. Windows XP używa UCS-2 (system Unicode) jako natywny typ łańcucha, co wydaje się być dla mnie oszukiwaniem. Na szczęście dla mnie postacie w większości nazw plików nie będą składać się z więcej niż jednego kodu źródłowego i myślę, że wszystkie moje były całkowicie łacińskie_1, całkowicie utf_8, lub po prostu ascii (który jest podzbiorem obu te). Więc po prostu je czytałem i dekodowałem tak, jakby były nadal w latin_1 lub utf_8. Możliwe jednak, że możesz mieć łacińskie_1 i utf_8 oraz dowolne inne znaki zmieszane razem w nazwie pliku w systemie Windows. Czasami te postacie mogą pojawiać się jako pudełka, innym razem wyglądają na zmanipulowane, a innym razem wyglądają poprawnie (znaki akcentowane i inne). Iść dalej.

(2) Python ma domyślne kodowanie systemowe, które jest ustawiane podczas uruchamiania Pythona i nie można go zmienić w czasie wykonywania. Aby uzyskać szczegółowe informacje, patrz here. Podsumowanie brudne ... dobrze tutaj jest plik I dodaje:

kodowania
\# sitecustomize.py 
\# this file can be anywhere in your Python path, 
\# but it usually goes in ${pythondir}/lib/site-packages/ 
import sys 
sys.setdefaultencoding('utf_8') 

Ten system to taki, który przyzwyczaja się podczas korzystania z Unicode („str”) Funkcja bez żadnych innych parametrów kodowania. Aby powiedzieć, że w inny sposób, python próbuje zdekodować "str" ​​do Unicode na podstawie domyślnego kodowania systemu.

(3) Jeśli używasz IDLE lub python linii poleceń, myślę, że twoja konsola wyświetli się zgodnie z domyślnym kodowaniem systemu. Używam pydev z eclipse z jakiegoś powodu, więc musiałem przejść do ustawień mojego projektu, edytować właściwości konfiguracji uruchamiania mojego skryptu testowego, przejść do zakładki Common i zmienić konsolę z Latin-1 na utf-8, aby Mogłem wizualnie potwierdzić, że to, co robię, działa.

(4) Jeśli chcesz mieć jakieś sznurki testowych, np

test_str = "ó" 

w kodzie źródłowym, a następnie trzeba będzie powiedzieć Pythona jaki rodzaj kodowania używasz w tym pliku. (FYI: kiedy literówka kodowania musiałem CTRL-Z, ponieważ mój plik stał się nieczytelny.) Można to łatwo osiągnąć poprzez umieszczenie linii podobnie jak w górnej części pliku z kodem źródłowym:

# -*- coding: utf_8 -*- 

Jeśli don „t mieć te informacje, próby Pythona do analizowania kodu jako ASCII domyślnie, a więc:

SyntaxError: Non-ASCII character '\xf3' in file _redacted_ on line 81, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details 

gdy program działa poprawnie, lub, jeśli nie używasz konsoli Pythona lub jakąkolwiek inną konsolę wyglądać na wyjściu, prawdopodobnie naprawdę będziesz obchodził tylko # 1 na liście. Domyślne ustawienia systemowe i kodowanie konsoli nie są aż tak ważne, chyba że musisz spojrzeć na wyjście i/lub używasz wbudowanej funkcji unicode() (bez żadnych parametrów kodowania) zamiast funkcji string.decode(). Napisałem funkcję demo, którą wkleję na dno tego gigantycznego bałaganu, który - jak mam nadzieję - poprawnie pokazuje elementy na mojej liście. Oto niektóre dane wyjściowe, gdy uruchamiam znak "ó" za pomocą funkcji demo, pokazując, jak różne metody reagują na znak jako dane wejściowe.Moje kodowanie systemu i wyjście konsoli są zarówno ustawione UTF_8 do tego biegu:

'�' = original char <type 'str'> repr(char)='\xf3' 
'?' = unicode(char) ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data 
'ó' = char.decode('latin_1') <type 'unicode'> repr(char.decode('latin_1'))=u'\xf3' 
'?' = char.decode('utf_8') ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data 

Teraz mogę zmienić system i kodowanie konsoli latin_1, a ja się tego wyjścia dla tego samego wejścia:

'ó' = original char <type 'str'> repr(char)='\xf3' 
'ó' = unicode(char) <type 'unicode'> repr(unicode(char))=u'\xf3' 
'ó' = char.decode('latin_1') <type 'unicode'> repr(char.decode('latin_1'))=u'\xf3' 
'?' = char.decode('utf_8') ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data 

Zwróć uwagę, że znak "oryginalny" wyświetla się poprawnie, a wbudowana funkcja unicode() działa teraz.

Teraz zmieniam dane wyjściowe z konsoli na utf_8.

'�' = original char <type 'str'> repr(char)='\xf3' 
'�' = unicode(char) <type 'unicode'> repr(unicode(char))=u'\xf3' 
'�' = char.decode('latin_1') <type 'unicode'> repr(char.decode('latin_1'))=u'\xf3' 
'?' = char.decode('utf_8') ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data 

Tutaj wszystko nadal działa tak samo jak ostatnio, ale konsola nie może poprawnie wyświetlić danych wyjściowych. Itd. Poniższa funkcja wyświetla również więcej informacji, które mogą pomóc komuś w zrozumieniu luki w ich rozumieniu. Wiem, że wszystkie te informacje znajdują się w innych miejscach i są tam dokładniej omówione, ale mam nadzieję, że byłby to dobry punkt wyjścia dla kogoś, kto próbuje uzyskać kodowanie za pomocą pythona i/lub sqlite. Pomysły są świetne, ale czasami kod źródłowy może zaoszczędzić ci dzień lub dwa, próbując dowiedzieć się, jakie funkcje działają.

Nota prawna: Nie jestem ekspertem od kodowania, umieściłem to razem, aby pomóc mojemu zrozumieniu. Ciągle się nad tym zastanawiałem, kiedy prawdopodobnie powinienem zacząć przekazywać funkcje jako argumenty, aby uniknąć tak bardzo zbędnego kodu, więc jeśli mogę, sprawię, że będzie on bardziej zwięzły. Ponadto, utf_8 i latin_1 nie są w żadnym wypadku jedynymi schematami kodowania, są tylko tymi dwoma, z którymi grałem, ponieważ myślę, że radzą sobie z wszystkim, czego potrzebuję. Dodaj własne schematy kodowania do funkcji demo i przetestuj własne dane wejściowe.

Jeszcze jedno: w systemie Windows jest trudne zadanie: apparently crazy application developers.

#!/usr/bin/env python 
# -*- coding: utf_8 -*- 

import os 
import sys 

def encodingDemo(str): 
    validStrings =() 
    try:   
     print "str =",str,"{0} repr(str) = {1}".format(type(str), repr(str)) 
     validStrings += ((str,""),) 
    except UnicodeEncodeError as ude: 
     print "Couldn't print the str itself because the console is set to an encoding that doesn't understand some character in the string. See error:\n\t", 
     print ude 
    try: 
     x = unicode(str) 
     print "unicode(str) = ",x 
     validStrings+= ((x, " decoded into unicode by the default system encoding"),) 
    except UnicodeDecodeError as ude: 
     print "ERROR. unicode(str) couldn't decode the string because the system encoding is set to an encoding that doesn't understand some character in the string." 
     print "\tThe system encoding is set to {0}. See error:\n\t".format(sys.getdefaultencoding()), 
     print ude 
    except UnicodeEncodeError as uee: 
     print "ERROR. Couldn't print the unicode(str) because the console is set to an encoding that doesn't understand some character in the string. See error:\n\t", 
     print uee 
    try: 
     x = str.decode('latin_1') 
     print "str.decode('latin_1') =",x 
     validStrings+= ((x, " decoded with latin_1 into unicode"),) 
     try:   
      print "str.decode('latin_1').encode('utf_8') =",str.decode('latin_1').encode('utf_8') 
      validStrings+= ((x, " decoded with latin_1 into unicode and encoded into utf_8"),) 
     except UnicodeDecodeError as ude: 
      print "The string was decoded into unicode using the latin_1 encoding, but couldn't be encoded into utf_8. See error:\n\t", 
      print ude 
    except UnicodeDecodeError as ude: 
     print "Something didn't work, probably because the string wasn't latin_1 encoded. See error:\n\t", 
     print ude 
    except UnicodeEncodeError as uee: 
     print "ERROR. Couldn't print the str.decode('latin_1') because the console is set to an encoding that doesn't understand some character in the string. See error:\n\t", 
     print uee 
    try: 
     x = str.decode('utf_8') 
     print "str.decode('utf_8') =",x 
     validStrings+= ((x, " decoded with utf_8 into unicode"),) 
     try:   
      print "str.decode('utf_8').encode('latin_1') =",str.decode('utf_8').encode('latin_1') 
     except UnicodeDecodeError as ude: 
      print "str.decode('utf_8').encode('latin_1') didn't work. The string was decoded into unicode using the utf_8 encoding, but couldn't be encoded into latin_1. See error:\n\t", 
      validStrings+= ((x, " decoded with utf_8 into unicode and encoded into latin_1"),) 
      print ude 
    except UnicodeDecodeError as ude: 
     print "str.decode('utf_8') didn't work, probably because the string wasn't utf_8 encoded. See error:\n\t", 
     print ude 
    except UnicodeEncodeError as uee: 
     print "ERROR. Couldn't print the str.decode('utf_8') because the console is set to an encoding that doesn't understand some character in the string. See error:\n\t",uee 

    print 
    print "Printing information about each character in the original string." 
    for char in str: 
     try: 
      print "\t'" + char + "' = original char {0} repr(char)={1}".format(type(char), repr(char)) 
     except UnicodeDecodeError as ude: 
      print "\t'?' = original char {0} repr(char)={1} ERROR PRINTING: {2}".format(type(char), repr(char), ude) 
     except UnicodeEncodeError as uee: 
      print "\t'?' = original char {0} repr(char)={1} ERROR PRINTING: {2}".format(type(char), repr(char), uee) 
      print uee  

     try: 
      x = unicode(char)   
      print "\t'" + x + "' = unicode(char) {1} repr(unicode(char))={2}".format(x, type(x), repr(x)) 
     except UnicodeDecodeError as ude: 
      print "\t'?' = unicode(char) ERROR: {0}".format(ude) 
     except UnicodeEncodeError as uee: 
      print "\t'?' = unicode(char) {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee) 

     try: 
      x = char.decode('latin_1') 
      print "\t'" + x + "' = char.decode('latin_1') {1} repr(char.decode('latin_1'))={2}".format(x, type(x), repr(x)) 
     except UnicodeDecodeError as ude: 
      print "\t'?' = char.decode('latin_1') ERROR: {0}".format(ude) 
     except UnicodeEncodeError as uee: 
      print "\t'?' = char.decode('latin_1') {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee) 

     try: 
      x = char.decode('utf_8') 
      print "\t'" + x + "' = char.decode('utf_8') {1} repr(char.decode('utf_8'))={2}".format(x, type(x), repr(x)) 
     except UnicodeDecodeError as ude: 
      print "\t'?' = char.decode('utf_8') ERROR: {0}".format(ude) 
     except UnicodeEncodeError as uee: 
      print "\t'?' = char.decode('utf_8') {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee) 

     print 

x = 'ó' 
encodingDemo(x) 

Bardzo dziękuję za odpowiedzi poniżej, a szczególnie za @Johna Machina za udzielenie odpowiedzi tak dokładnie.

Odpowiedz

34

I'm still ignorant of whether there is a way to correctly convert 'ó' from latin-1 to utf-8 and not mangle it

repr() i unicodedata.name() są twoi przyjaciele, jeśli chodzi o takie problemy debugowania:

>>> oacute_latin1 = "\xF3" 
>>> oacute_unicode = oacute_latin1.decode('latin1') 
>>> oacute_utf8 = oacute_unicode.encode('utf8') 
>>> print repr(oacute_latin1) 
'\xf3' 
>>> print repr(oacute_unicode) 
u'\xf3' 
>>> import unicodedata 
>>> unicodedata.name(oacute_unicode) 
'LATIN SMALL LETTER O WITH ACUTE' 
>>> print repr(oacute_utf8) 
'\xc3\xb3' 
>>> 

Jeśli wyślesz oacute_utf8 do terminalu skonfigurowanego dla Latin1, otrzymasz A-tyldę, a następnie indeks górny-3.

I switched to Unicode strings.

Co nazywasz ciągami Unicode? UTF-16?

What gives? After reading this, describing exactly the same situation I'm in, it seems as if the advice is to ignore the other advice and use 8-bit bytestrings after all.

Nie mogę sobie wyobrazić, jak ci się wydaje. Historia, którą przekazywano, polegała na tym, że obiekty unicode w Pythonie i kodowanie UTF-8 w bazie danych były do ​​zrobienia. Jednak Martin odpowiedział na oryginalne pytanie, podając metodę ("fabryka tekstów") dla OP, aby móc używać łacińskiego1 - to NIE stanowiło rekomendacji!

Aktualizacja w odpowiedzi na dalsze pytania podniesione w komentarzu:

I didn't understand that the unicode characters still contained an implicit encoding. Am I saying that right?

No.Kodowanie to odwzorowanie między Unikodem i czymś innym, i na odwrót. Znak Unicode nie ma kodowania, domyślnego ani żadnego innego.

It looks to me like unicode("\xF3") and "\xF3".decode('latin1') are the same when evaluated with repr().

Powiedz co? To nie wygląda na to, aby mnie:

>>> unicode("\xF3") 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf3 in position 0: ordinal 
not in range(128) 
>>> "\xF3".decode('latin1') 
u'\xf3' 
>>> 

Może chodziło: u'\xf3' == '\xF3'.decode('latin1') ... to jest na pewno prawdziwe.

Prawdą jest również, że unicode(str_object, encoding) działa tak samo, jak str_object.decode(encoding) ... włącznie z nadmuchiwaniem, gdy dostarczane jest niewłaściwe kodowanie.

Is that a happy circumstance

że pierwsze 256 znaków Unicode są takie same, kod do kodu, jak 256 znaków latin1 jest dobrym pomysłem. Ponieważ wszystkie 256 możliwych znaków łacińskich1 jest zamapowanych na kod Unicode, oznacza to, że DOWOLNY 8-bitowy bajt, dowolny obiekt stritu Python może zostać zdekodowany na kod Unicode bez podnoszenia wyjątku. Tak właśnie powinno być.

Istnieją jednak pewne osoby, które mylą dwa zupełnie odrębne pojęcia: "mój scenariusz przebiega do końca bez żadnych wyjątków, które są podnoszone" i "mój skrypt jest wolny od błędów". Dla nich łaciński 1 jest "sidłem i złudzeniem".

Innymi słowy, jeśli posiadasz plik, który jest faktycznie zakodowany w cp1252 lub gbk lub koi8-u lub cokolwiek innego i rozszyfrujesz go za pomocą latin1, wynikowy kod Unicode będzie zupełnym śmieciem, a Python (lub jakikolwiek inny język) nie będzie Zgłoś błąd - nie ma możliwości dowiedzenia się, że popełniłeś głupotę.

or is unicode("str") going to always return the correct decoding?

tak po prostu, z domyślnego kodowania ma ASCII, zwróci poprawną Unicode, jeśli plik jest rzeczywiście zakodowane w kodzie ASCII. W przeciwnym razie wybuchnie.

Podobnie, jeśli podasz prawidłowe kodowanie, lub takie, które jest nadzbiorem właściwego kodowania, otrzymasz poprawny wynik. W przeciwnym razie dostaniesz bełkot lub wyjątek.

W skrócie: odpowiedź brzmi nie.

If not, when I receive a python str that has any possible character set in it, how do I know how to decode it?

Jeśli obiekt str jest prawidłowym dokumentem XML, zostanie podany z góry. Domyślną wartością jest UTF-8. Jeśli jest to odpowiednio skonstruowana strona internetowa, należy ją podać z góry (poszukaj "zestawu znaków"). Niestety wielu autorów stron internetowych leży w zębach (ISO-8859-1 aka latin1, powinno być Windows-1252 aka cp1252, nie marnuj zasobów próbując odkodować gb2312, zamiast tego użyj gbk). Możesz uzyskać wskazówki od narodowości/języka strony internetowej.

UTF-8 zawsze warto spróbować. Jeśli dane są ascii, będzie działało dobrze, ponieważ ascii jest podzbiorem utf8. Ciąg tekstowy, który został napisany przy użyciu znaków innych niż ASCII i został zakodowany w kodowaniu innym niż utf8, prawie na pewno zawiedzie z wyjątkiem, jeśli spróbujesz go zdekodować jako utf8.

Wszystkie powyższe heurystyki i więcej oraz wiele statystyk są zawarte w chardet, module do odgadywania kodowania dowolnych plików. Zwykle działa dobrze. Jednak nie można uczynić oprogramowania idiotoodpornym. Na przykład, jeśli połączysz pliki danych napisane trochę za pomocą kodowania A, a niektóre za pomocą kodu B, i podasz wynik do chardet, odpowiedź będzie prawdopodobnie kodować C ze zmniejszonym poziomem zaufania, np. 0,8. Zawsze sprawdzaj część zaufania dotyczącą odpowiedzi:.

Jeśli wszystko inne zawiedzie:

(1) Spróbuj zapytać tutaj, z małej próbki z przodu swoich danych ... print repr(your_data[:400]) ... i cokolwiek zabezpieczenie informacji o jego pochodzeniu, że masz.

(2) Najnowsze rosyjskie badania nad techniques for recovering forgotten passwords wydają się mieć zastosowanie do wnioskowania o nieznanych kodowaniach.

Aktualizacja 2 BTW, nie jest to czas, żebyś otworzyła się kolejne pytanie -)

One more thing: there are apparently characters that Windows uses as Unicode for certain characters that aren't the correct Unicode for that character, so you may have to map those characters to the correct ones if you want to use them in other programs that are expecting those characters in the right spot.

To nie Windows, który to robi; to grupa szalonych twórców aplikacji. Można mieć bardziej zrozumiałych względów nie parafrazując ale cytowany pierwszym akapicie artykułu effbot które dotyczyły:

Some applications add CP1252 (Windows, Western Europe) characters to documents marked up as ISO 8859-1 (Latin 1) or other encodings. These characters are not valid ISO-8859-1 characters, and may cause all sorts of problems in processing and display applications.

Tło:

Zakres U + 0000 do U + 001F inclusive jest wyznaczony w Unicode jako " C0 znaki kontrolne ". Istnieją one również w ASCII i latin1, z tymi samymi znaczeniami. Obejmują one takie znane rzeczy, jak powrót karetki, podawanie linii, dzwonek, backspace, karta i inne, które są rzadko używane.

Zakres od U + 0080 do U + 009F włącznie jest oznaczony w Unicode jako "Znaki sterujące C1". Te istnieją również w latin1 i zawierają 32 znaki, których nikt poza unicode.org nie może sobie wyobrazić.

W związku z tym, jeśli uruchomisz liczbę częstotliwości znaków na danych Unicode lub Latin1 i znajdziesz wszystkie znaki w tym zakresie, Twoje dane są uszkodzone. Nie ma uniwersalnego rozwiązania; zależy to od tego, jak został uszkodzony. Znaki mogą mają to samo znaczenie, co znaki cp1252 w tych samych pozycjach, dlatego rozwiązanie effbot będzie działało. W innym przypadku, którego ostatnio szukałem, wydaje się, że podejrzane postacie zostały spowodowane połączeniem plików tekstowych zakodowanych w UTF-8 i innym kodowaniem, które musiało zostać wydedukowane na podstawie częstotliwości liter w języku (ludzkim), napisany.

+0

miałem moją konfigurację terminala na latin1, więc zmienia. Kiedy pytasz, co nazywam ciągami Unicode, użyłem metody unicode() na str, z której czytałem. Nie rozumiałem, że znaki unicode nadal zawierają niejawne kodowanie. Czy mówię to tak? Wygląda na to, że kod unicode ("\ xF3") i "\ xF3" .decode ("latin1") są takie same, gdy zostały obliczone za pomocą funkcji repr(). Czy to jest szczęśliwa okoliczność, czy też unicode ("str") zawsze zwróci poprawne dekodowanie? Jeśli nie, to kiedy otrzymam pytona, który ma ustawiony dowolny zestaw znaków, skąd mam wiedzieć, jak to rozszyfrować? –

+0

Jeszcze raz dziękuję. Domyślam się, że odpowiedź na twoje pytanie "Co nazywasz ciągami Unicode? UTF-16?" jest "czymkolwiek jest Oacute_unicode". Czy brakuje mi jakiegoś aspektu twojego pytania, które powinienem zrozumieć? Dekodowałem ciągi znaków otrzymane z nazw plików i wysyłałem je do bazy danych w tej formie. Zamówiłem też dziesięć z tamburynem Pentagonu, więc wkrótce będę miał możliwości dekodowania wojskowego, a python będzie niepotrzebny. –

+0

Właśnie sprawdzałem, czy naprawdę chodzi o obiekty unicode Pythona == czym jest Oacute_unicode, a nie o UTF-16, które jest inną możliwością kodowania w bazach SQLite i które niektórzy nazywają "Unicode". –

7

Oczywiście, że istnieje. Ale dane są już złamane w bazie danych, więc trzeba go naprawić:

>>> print u'Sigur Rós'.encode('latin-1').decode('utf-8') 
Sigur Rós 
20

UTF-8 jest domyślnym kodowaniem baz danych SQLite. To pojawia się w takich sytuacjach jak "SELECT CAST (x'52C3B373" AS TEXT); ". Jednak biblioteka SQLite C faktycznie nie sprawdza, czy łańcuch wstawiony do DB jest prawidłowym UTF-8.

Po wstawieniu obiektu Unicode w języku Python (lub obiektu str w wersji 3.x) biblioteka Python sqlite3 automatycznie przekonwertuje ją do formatu UTF-8. Ale jeśli wstawisz obiekt str, to tylko przyjmie ciąg znaków UTF-8, ponieważ Python 2.x "str" ​​nie zna jego kodowania. Jest to jeden z powodów, dla których preferowane są ciągi Unicode.

Jednak nie pomaga, jeśli na początku dane zostaną zerwane.

Aby naprawić swoje dane, zrobić

db.create_function('FIXENCODING', 1, lambda s: str(s).decode('latin-1')) 
db.execute("UPDATE TheTable SET TextColumn=FIXENCODING(CAST(TextColumn AS BLOB))") 

dla każdej kolumny tekstu w swojej bazie danych.

+0

To naprawdę przydatne. Nie potrzebuję tego teraz, ponieważ próbuję zrozumieć te rzeczy, a moja db jest do dyspozycji, ale wciąż zasługuje na upomink dla użyteczności. –

+0

Napisałem krótki skrypt, który używa tej techniki do ponownego kodowania wszystkich kolumn 'text',' clob' i 'char', we wszystkich tabelach docelowej bazy danych. http://stackoverflow.com/a/29048500/1191425. –

18

naprawiłem ten problem pysqlite przez ustawienie:

conn.text_factory = lambda x: unicode(x, 'utf-8', 'ignore') 

Domyślnie text_factory jest ustawione na Unicode(), która będzie korzystać z aktualnego domyślnego kodowania (ASCII na moim komputerze)

+0

Chociaż 'text_factory' jest wspomniany w błędzie, który dostaję, nie mogę znaleźć żadnego odniesienia do niego w SQL Alchemy v0.7.6 online docs na http://docs.sqlalchemy.org/en/latest/ – RobM

+0

Zrobiłem to, ale to wciąż narzeka. – fiatjaf

+0

Świetnie! Tylko mała zmiana w moim przypadku: usuwanie znaków z akcentem ... więc użyłem 'conn.text_factory = lambda x: x.decode ('latin-1')' – luc

2

moich problemów Unicode 2.x Pythona (Python 2.7.6 za szczególne) ustalona następująco:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

from __future__ import unicode_literals 
import sys 
reload(sys) 
sys.setdefaultencoding('utf-8') 

on rozwiązany również błąd Natomiast co prawda na początku postu:

sqlite3.ProgrammingError: You must not use 8-bit bytestrings unless ...

EDIT

sys.setdefaultencoding jest brudny Hack. Tak, może rozwiązać problemy z UTF-8, ale wszystko ma swoją cenę. Aby uzyskać więcej informacji można znaleźć w poniższych linków:

+1

Nie używaj 'sys.setdefaultencoding() '. To okropne kultywowanie ładunków i służy tylko do * tymczasowego ukrywania złamania *. Przerywa inne rzeczy. Właśnie spryskujesz nad siniakami, zamiast przestać być uderzany przez ciosy. Zamiast tego przestań robić dziurkowania i poprawnie obsługuj unicode. –