2016-08-09 18 views
7

ja zapytań do bazy danych MySQL z SQLAlchemy i otrzymuję następujący błąd:UnicodeDecodeError Ładowanie z SQLAlchemy

UnicodeDecodeError: 'utf8' codec can't decode bytes in position 498-499: unexpected end of data 

kolumna w tabeli zdefiniowano jako Unicode(500) więc ten błąd sugeruje mi, że nie jest to pozycja, która była skrócone, ponieważ zawierało więcej niż 500 znaków. Czy istnieje sposób, aby obsłużyć ten błąd i nadal ładować wpis? Czy istnieje sposób na odnalezienie błędnego wpisu i usunięcie go poza próbą załadowania każdego wpisu pojedynczo (lub partiami), dopóki nie otrzymam błędu?

+0

Wygląda na to, że cokolwiek wykonano, obcięcie było tak nieświadome zestawu znaków, co było oryginalnym błędem. Możesz spróbować przekonwertować kolumnę MySQL na binarną, a następnie z powrotem na UTF8-I * myślę, że powinna wymusić zastąpienie niekompletnych znaków przez '?'. – eggyal

+2

Możesz zacząć od włączenia pełnego śledzenia; w ten sposób możemy przynajmniej zdiagnozować, czy dekodowanie może być wykonane w innym miejscu, czy też można je skonfigurować tak, aby zajęło się procedurą obsługi błędów. –

Odpowiedz

0

Uczyń przechowywaną kolumnę jako BLOB. Po załadowaniu danych, robić różne rzeczy takie jak

SELECT MAX(LENGTH(col)) FROM ... -- to see what the longest is in _bytes_. 

skopiować dane do innej kolumny BLOB i zrobić

ALTER TABLE t MODIFY col2 TEXT CHARACTER SET utf8 ... -- to see if it converts correctly 

Jeśli to się powiedzie, wówczas zrobić

SELECT MAX(CHAR_LENGTH(col2)) ... -- to see if the longest is more than 500 _characters_. 

Po wypróbowaniu kilka rzeczy w tym stylu, możemy zobaczyć, w którym kierunku dalej.

2

Krótko mówiąc, należy zmienić:

Unicode(500) 

do: (. Python 2 kod poniżej, ale zasady trzymać w python 3; tylko niektóre wyjścia będą się różnić)

Unicode(500, unicode_errors='ignore', convert_unicode='force') 

Co się dzieje, kiedy dekodujesz testowanie, skarży się, że nie można dekodować testu bytowego z powodu błędu, który widziałeś.

>>> u = u'ABCDEFGH\N{TRADE MARK SIGN}' 
>>> u 
u'ABCDEFGH\u2122' 
>>> print(u) 
ABCDEFGH™ 
>>> s = u.encode('utf-8') 
>>> s 
'ABCDEFGH\xe2\x84\xa2' 
>>> truncated = s[:-1] 
>>> truncated 
'ABCDEFGH\xe2\x84'   
>>> truncated.decode('utf-8') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/Users/cliffdyer/.virtualenvs/edx-platform/lib/python2.7/encodings/utf_8.py", 
line 16, in decode 
    return codecs.utf_8_decode(input, errors, True) 
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 8-9: unexpected 
end of data 

Python zapewnia jednak różne opcjonalne tryby obsługi błędów dekodowania. Zgłaszanie wyjątków jest domyślne, ale można również obcinać tekst lub przekształcać zniekształconą część ciągu w oficjalny znak zastępczy Unicode.

>>> trunc.decode('utf-8', errors='replace') 
u'ABCDEFGH\ufffd' 
>>> trunc.decode('utf-8', errors='ignore') 
u'ABCDEFGH' 

To jest dokładnie to, co dzieje się w ramach obsługi kolumn.

Patrząc na klasy Unicode i String w sqlalchemy/sql/sqltypes.py, wygląda na to, że istnieje argument unicode_errors, który można przekazać do konstruktora, który przekazuje jego wartość do argumentu błędów kodera. Istnieje również uwaga, że ​​musisz ustawić convert_unicode='force', aby działało.

W ten sposób Unicode(500, unicode_errors='ignore', convert_unicode='force') powinien rozwiązać twój problem, jeśli jesteś w porządku z obcięciem końce danych.

Jeśli masz pewną kontrolę nad bazą danych, powinieneś być w stanie zapobiec temu problemowi w przyszłości, definiując bazę danych, aby użyć zestawu znaków utf8mb4. (Nie używaj po prostu utf8, lub nie powiedzie się na czterobajtowych utf8 znakach, wliczając w to większość emoji). Wtedy będziesz mieć gwarancję, że masz ważny utf-8 zapisany i zwrócony z twojej bazy danych.

0

Podsumowując, twoja konfiguracja MySQL jest niepoprawna, ponieważ obcina znaki UTF-8 w połowie sekwencji.Dwukrotnie sprawdziłbym, czy MySQL rzeczywiście oczekuje kodowania znaków UTF-8 w sesjach i samych tabelach.


proponuję przejście do PostgreSQL (poważnie), aby uniknąć tego rodzaju problemu: nie tylko PostgreSQL zrozumieć UTF-8 poprawnie w konfiguracji domyślnej, ale również nie byłoby nigdy obciąć ciąg, aby pasowały do ​​wartości , decyduje się podnieść błąd zamiast:

psql (9.5.3, server 9.5.3) 
Type "help" for help. 

testdb=> create table foo(bar varchar(4)); 
CREATE TABLE 
testdb=> insert into foo values ('aaaaa'); 
ERROR: value too long for type character varying(4) 

To również nie jest w przeciwieństwie do Zen Pythona:

Explicit is better than implicit.

i

Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.