UnicodeDecodeError: kodek „ascii” nie może zdekodować bajtu 0xd1 na pozycji 2: numer porządkowy poza zakresem (128)

107

Próbuję pracować z bardzo dużym zbiorem danych, który zawiera kilka niestandardowych znaków. Muszę użyć Unicode, zgodnie ze specyfikacją pracy, ale jestem zdziwiony. (I całkiem możliwe, że robię to wszystko źle.)

Otwieram CSV za pomocą:

 15     ncesReader = csv.reader(open('geocoded_output.csv', 'rb'), delimiter='\t', quotechar='"')

Następnie próbuję zakodować go za pomocą:

name=school_name.encode('utf-8'), street=row[9].encode('utf-8'), city=row[10].encode('utf-8'), state=row[11].encode('utf-8'), zip5=row[12], zip4=row[13],county=row[25].encode('utf-8'), lat=row[22], lng=row[23])

Koduję wszystko oprócz lat i lng, ponieważ muszą one zostać wysłane do API. Kiedy uruchamiam program, aby przeanalizować zestaw danych do tego, czego mogę użyć, otrzymuję następujący Traceback.

Traceback (most recent call last):
  File "push_into_db.py", line 80, in <module>
    main()
  File "push_into_db.py", line 74, in main
    district_map = buildDistrictSchoolMap()
  File "push_into_db.py", line 32, in buildDistrictSchoolMap
    county=row[25].encode('utf-8'), lat=row[22], lng=row[23])
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd1 in position 2: ordinal not in range(128)

Myślę, że powinienem powiedzieć, że używam Pythona 2.7.2 i jest to część aplikacji zbudowanej na django 1.4. Przeczytałem kilka postów na ten temat, ale żaden z nich nie wydaje się mieć bezpośredniego zastosowania. Każda pomoc będzie bardzo mile widziana.

Możesz również chcieć wiedzieć, że niektóre niestandardowe znaki powodujące problem to Ñ i prawdopodobnie É.

jelkimantis
źródło
1
Jakie jest Twoje oryginalne kodowanie pliku? Myślę, że powinieneś zdekodować go zgodnie z oryginalnym kodowaniem, a następnie przekonwertować na utf 8
xiao 啸
możliwy duplikat kodu Encoding daje kodek „ascii” nie może zakodować znaku… numer porządkowy poza zakresem (128) ” [red .: i jestem pewien, że jest to również około miliarda innych]
Karl Knechtel

Odpowiedzi:

152

Unicode nie jest równy UTF-8. To drugie jest tylko kodowaniem dla pierwszego.

Robisz to w niewłaściwy sposób. Jesteś czytanie UTF-8 zakodowane dane, więc trzeba dekodowania UTF-8 zakodowany ciąg na ciąg znaków Unicode.

Więc po prostu zastąpić .encodez .decode, i to powinno działać (jeśli .csv jest UTF-8-zakodowane).

Nie ma się jednak czego wstydzić. Założę się, że 3 na 5 programistów miało na początku problemy ze zrozumieniem tego, jeśli nie więcej;)

Aktualizacja: Jeśli twoje dane wejściowe nie są zakodowane w UTF-8, musisz .decode()oczywiście z odpowiednim kodowaniem. Jeśli nic nie zostanie podane, python zakłada ASCII, co oczywiście kończy się niepowodzeniem w przypadku znaków innych niż ASCII.

ch3ka
źródło
1
Powodem błędu jest to, że Python próbuje automatycznie zdekodować go z domyślnego kodowania ASCII, aby następnie mógł go zakodować tak, jak określił, do UTF-8. Ponieważ dane nie są poprawnym ASCII, to nie działa.
rg
7
jasne, ale jeśli są to dane zakodowane w UTF8 (jak sądzę), to .decode('utf-8')powinno załatwić sprawę , ani?
ch3ka
Jasne, prawdopodobnie masz rację. Właśnie wyjaśniałem, dlaczego pojawia się ten konkretny błąd w tej sytuacji.
środa
1
Idealny! Dziękuję Ci bardzo. Okazuje się więc, że to był plik .decode ('latin-1') - to ma sens, ponieważ to Ñ sprawiało mi problem. Jeszcze raz! Dziękuję Ci!
jelkimantis
Twoje rozwiązanie działa w niektórych przypadkach, ale jeśli go użyję, pojawia
Vikash Mishra
84

Po prostu dodaj te linie do swoich kodów:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')
khelili miliana
źródło
5
`AttributeError: module 'sys' nie ma atrybutu 'setdefaultencoding' nie działa w Pythonie 3
skjerns
Woot woot! To mi pomogło.
Shougo Makishima,
1
Działa dla mojego Pythona 2.7, uwaga, potrzebne jest przeładowanie (sys), w przeciwnym razie setdefaultencoding nie byłby dostępny.
Yu Shen
1
To była jedyna rzecz, która sprawiła, że ​​zadziałało na mnie z wielu pytań SO. Dzięki wielkie!
Freedo
nazwa „przeładuj” nie jest zdefiniowana
Davide
28

dla użytkowników Pythona 3. możesz to zrobić

with open(csv_name_here, 'r', encoding="utf-8") as f:
    #some codes

działa też z kolbą :)

Skrmnghrd
źródło
1
To pierwszy raz, kiedy komuś tu pomogłem. dobrze się czuje wiedząc, że pomogłem :)
Skrmnghrd
1
Pomogłeś też mi :) Wszystkie inne odpowiedzi nie działały przy czytaniu plików. Teraz muszę się dowiedzieć, jak to naprawić również do pisania;)
user2194898
czy możesz przesłać mi link do swojego kodu? Spróbuję pomóc
Skrmnghrd
9

Głównym powodem błędu jest to, że domyślnym kodowaniem przyjętym przez Pythona jest ASCII. W związku z tym, jeśli dane ciągu, które mają być zakodowane, encode('utf8')zawierają znak spoza zakresu ASCII, np. Dla łańcucha takiego jak „hgvcj 터 파크 387”, Python zgłosi błąd, ponieważ łańcuch nie jest w oczekiwanym formacie kodowania.

Jeśli używasz wersji Pythona wcześniejszej niż 3.5, niezawodną poprawką byłoby ustawienie domyślnego kodowania przyjętego przez Pythona na utf8:

import sys
reload(sys)
sys.setdefaultencoding('utf8')
name = school_name.encode('utf8')

W ten sposób Python byłby w stanie przewidzieć znaki w ciągu, które wykraczają poza zakres ASCII.

Jeśli jednak używasz Pythona w wersji 3.5 lub nowszej, funkcja reload () nie jest dostępna, więc musiałbyś to naprawić za pomocą dekodowania, np.

name = school_name.decode('utf8').encode('utf8')
Temi Fakunle
źródło
jaka jest różnica między twoją a moją odpowiedzią
khelili miliana
1
Bardziej szczegółowe. Ludziom często pomagają szczegóły przyczynowe. Twój kod działa przy okazji, bez odstępstw.
Temi Fakunle
1
funkcja reload jest dostępna w Pythonie 3, wystarczy ją zaimportować. z importu imp reload
Meow
@Meow, ale nie ma sys.setdefaultencoding w Pythonie 3. Więc w kontekście kompatybilności py2 \ py3 wystarczy trochę sprawdzenia, może sys.getdefaultencoding (). Byłbym wdzięczny za radę w tej sprawie. stackoverflow.com/questions/28127513/…
Konst54
2

Dla użytkowników Python 3:

zmiana kodowania z „ascii” na „latin1” działa.

Możesz również spróbować znaleźć kodowanie automatycznie, odczytując pierwsze 10000 bajtów za pomocą poniższego fragmentu:

import chardet  
with open("dataset_path", 'rb') as rawdata:  
            result = chardet.detect(rawdata.read(10000))  
print(result)
Prithvi
źródło
2

Mój komputer miał ustawione niewłaściwe ustawienia regionalne.

Ja pierwszy

>>> import locale
>>> locale.getpreferredencoding(False)
'ANSI_X3.4-1968'

locale.getpreferredencoding(False)jest funkcją wywoływaną przez, open()gdy nie podajesz kodowania . Wyjście powinno być 'UTF-8', ale w tym przypadku jest to jakiś wariant ASCII .

Następnie uruchomiłem polecenie bash localei otrzymałem to wyjście

$ locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

Tak więc korzystałem z domyślnych ustawień regionalnych Ubuntu, co powoduje, że Python otwiera pliki jako ASCII zamiast UTF-8. Musiałem ustawić ustawienia regionalne naen_US.UTF-8

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales

Jeśli nie możesz zmienić ustawień regionalnych w całym systemie, możesz wywołać cały kod Pythona w następujący sposób:

PYTHONIOENCODING="UTF-8" python3 ./path/to/your/script.py

lub zrób

export PYTHONIOENCODING="UTF-8"

aby ustawić go w powłoce, w której to uruchomisz.

Boris
źródło
1

Jeśli napotkasz ten problem podczas uruchamiania certbot podczas tworzenia lub odnawiania certyfikatu, użyj następującej metody

grep -r -P '[^\x00-\x7f]' /etc/apache2 /etc/letsencrypt /etc/nginx

To polecenie znalazło obraźliwy znak „´” w jednym pliku .conf w komentarzu. Po usunięciu go (możesz edytować komentarze, jak chcesz) i przeładowaniu nginx, wszystko działało ponownie.

Źródło: https://github.com/certbot/certbot/issues/5236

Anish Varghese
źródło
0

Lub kiedy masz do czynienia z tekstem w Pythonie, jeśli jest to tekst Unicode, zanotuj, że jest to Unicode.

text=u'unicode text'Zamiast tego ustaw po prostu text='unicode text'.

To zadziałało w moim przypadku.

prosti
źródło
0

otwarte z kodowaniem UTF 16 ze względu na szerokość i długość.

with open(csv_name_here, 'r', encoding="utf-16") as f:
karthik r
źródło
0

Działa po prostu przyjmując argument „rb” do odczytu binarnego zamiast „r” do odczytu

Jose Garcia-Uceda
źródło