2009-07-27 12 views
18

Próbuję przetłumaczyć arkusz kalkulacyjny programu Excel do pliku CSV przy użyciu modułów xlrd i csv w języku Python, ale odkładam słuchawkę na problemy z kodowaniem. Xlrd generuje dane wyjściowe z Excela w Unicode, a moduł CSV wymaga UTF-8.Unicode do UTF8 dla plików CSV - Python przez xlrd

Wyobrażam sobie, że nie ma to nic wspólnego z modułem xlrd: wszystko działa dobrze, wyprowadzając na stdout lub inne wyjścia, które nie wymagają określonego kodowania.

Arkusz jest zakodowany jako UTF-16-LE, według book.encoding

Uproszczona wersja tego, co robię jest:

from xlrd import * 
import csv 
b = open_workbook('file.xls') 
s = b.sheet_by_name('Export') 
bc = open('file.csv','w') 
bcw = csv.writer(bc,csv.excel,b.encoding) 
for row in range(s.nrows): 
    this_row = [] 
    for col in range(s.ncols): 
     this_row.append(s.cell_value(row,col)) 
    bcw.writerow(this_row) 

To daje następujący błąd, około 740 linii w :

UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 5: ordinal not in range(128) 

wartość ta wydaje się być coraz powiesił na to „516-777316” - tekst w oryginalnym arkuszu Excel jest „516-7773167” (z 7 na końcu)

Będę pierwszym, który przyzna, że ​​mam tylko mgliste poczucie, jak działa kodowanie znaków, więc większość tego, co już próbowałem tak daleko są różne permutacje przebiera wśród .encode i .decode on the s.cell_value(row,col)

Gdyby ktoś mógł zaproponować rozwiązanie, byłbym wdzięczny - jeszcze lepiej, gdybyś mógł wyjaśnić, co nie działa i dlaczego, abym mógł łatwiej rozwiązywać te problemy samodzielnie w przyszłości.

Z góry dziękuję!

EDIT:

Dzięki za komentarze do tej pory.

Gdy ja użytkownika this_row.append(s.cell(row,col)) (np. S.cell zamiast s.cell_value) cały dokument zapisuje bez błędów.

Wynik nie jest szczególnie pożądany (text:u'516-7773167'), ale pozwala uniknąć błędu, nawet jeśli znaki powodujące obrażenia są nadal widoczne na wyjściu.

To sprawia, że ​​myślę, że wyzwanie może być po Xlrd.

Myśli?

+0

Byłoby przydatne, aby zobaczyć cały traceback, aby wiedzieć, kto rzuca błąd. – Christopher

+0

Niewiele więcej do zobaczenia: Plik "the_script.py ", wiersz 40, w this_row.append (str (s.cell_value (row, col))) UnicodeEncodeError: kodek 'ascii' nie może kodować znaków u '\ xed' w pozycji 5: porządkowy nie w zakresie (128) – anschauung

+0

"Wyjście z Excela w Unicode" wydaje się oznaczać "wyjście z Excela w UTF-16". Unicode definiuje przestrzeń kodowania, która jest reprezentowana przez różne systemy kodowania, takie jak UTF-8 lub UTF-16. – Svante

Odpowiedz

25

Spodziewam wartość zwracana cell_value jest ciąg Unicode, która powoduje problemy (prosimy o wydrukowanie jej type() aby potwierdzić), w takim przypadku powinny być w stanie go rozwiązać, zmieniając tę ​​jedną linię:

this_row.append(s.cell_value(row,col)) 

do:

this_row.append(s.cell_value(row,col).encode('utf8')) 

Jeśli cell_value wraca wiele różnych rodzajów, to trzeba kodować tylko wtedy, gdy jest to powrót ciąg unicode; więc, że ten wiersz podzielony na kilka linii:

val = s.cell_value(row, col) 
if isinstance(val, unicode): 
    val = val.encode('utf8') 
this_row.append(val) 
+0

Idealnie! Tak było. Przypuszczam, że nie spodziewałem się, że różne typy wartości będą musiały być traktowane inaczej. Dzięki! – anschauung

0

Wygląda na dwie możliwości. Jednym z nich jest to, że prawdopodobnie nie otworzyłeś prawidłowo pliku wyjściowego:

"Jeśli plik csv jest obiektem pliku, musi być otwarty z flagą" b "na platformach, gdzie to robi różnicę." (http://docs.python.org/library/csv.html#module-csv)

Jeśli to nie jest problem, to kolejna opcja dla Ciebie jest użycie codecs.EncodedFile (plik [wejście, wyjście, błędy []]) jako owijki do wyjścia Twój .csv:

http://docs.python.org/library/codecs.html#module-codecs

To pozwoli ci mieć filtr obiektów plików z przychodzącego UTF16 do UTF8. Podczas gdy oba są technicznie "unicode", sposób ich kodowania jest bardzo różny.

coś takiego:

rbc = open('file.csv','w') 
bc = codecs.EncodedFile(rbc, "UTF16", "UTF8") 
bcw = csv.writer(bc,csv.excel) 

może rozwiązać problem dla ciebie, zakładając zrozumiałem problem prawo, a przy założeniu, że zostanie zgłoszony błąd podczas zapisywania danych do pliku.

+0

Cóż, podał inny komunikat o błędzie (tym razem przed zapisaniem czegokolwiek do pliku): UnicodeDecodeError: Kodek 'utf16' nie może dekodować bajtu 0x0a na pozycji 938: obcięte dane – anschauung

0

Wygląda na to, że masz 2 problemy.

W tej komórce coś jest zakorkowane - "7" powinno być zakodowane jako u'x37 "Myślę, że skoro jest w zakresie ASCII.

Co ważniejsze jednak, fakt, że dostajesz komunikat o błędzie, określając, że kodek ascii nie może być stosowany sugeruje, że coś jest nie tak z kodowania na Unicode - to myśli, że próbujesz zakodować wartość 0xed które mogą nie będzie reprezentowany w ASCII, ale powiedziałeś, że próbujesz reprezentować go w Unicode.

Nie jestem wystarczająco sprytny, aby ustalić, która linia powoduje problem - jeśli edytujesz pytanie, aby poinformować mnie, która linia powoduje ten komunikat o błędzie, być może będę w stanie pomóc nieco więcej (domyślam się, że to albo this_row.append(s.cell_value(row,col)) lub bcw.writerow(this_row), ale byłbym wdzięczny za potwierdzenie).

+0

Dzięki! Błąd jest na bcw.writerow. Wszystko wypada poprawnie, jeśli, powiedzmy, używam print this_row. Jak najlepiej mogę powiedzieć, nie ma nic oczywistego z "7" - poprawnie wyświetla się (jako u'516-7773167) kiedy drukuję na standardowe wyjście. – anschauung

+0

Następnie wygląda na to, że 'bcw.writerow' spodziewa się ASCII - czy jesteś pewien, że twoje argumenty są poprawne dla" csv.writer "(zobacz http://docs.python.org/library/csv.html#csv. pisarz)? Jestem oszołomiony, skąd pochodzi "0xed". –

9

Pytałeś o wyjaśnienia, ale niektóre z tych zjawisk są niewytłumaczalne bez twojej pomocy.

(A) Łańcuchy w plikach XLS utworzone w programie Excel 97 są kodowane w języku łacińskim1, jeśli to możliwe, w przeciwnym razie w UTF16LE. Każdy ciąg nosi flagę informującą, który był używany. Wcześniej Wyróżnia zakodowane ciągi zgodnie z "stroną kodową" użytkownika. W każdym przypadku, xlrd generuje obiekty Unicode. Kodowanie plików jest interesujące tylko wtedy, gdy plik XLS został utworzony przez oprogramowanie innej firmy, które albo pomija stronę kodową, albo kłamie na jej temat. Zobacz sekcję Unicode z przodu dokumentów xlrd.

(B) Niewyjaśnione zjawiska:

ten kod:

bcw = csv.writer(bc,csv.excel,b.encoding) 

powoduje następujący błąd z Pythona 2.5, 2.6 i 3.1: TypeError: expected at most 2 arguments, got 3 - to o co ja się spodziewać biorąc pod uwagę docs na csv.writer; spodziewa się obiektu filelike, a następnie (1) nic (2) dialekt lub (3) jeden lub więcej parametrów formatowania. Dałeś mu dialekt, a csv.writer nie ma argumentu kodowania, więc splat. Jakiej wersji Pythona używasz? A może nie skopiowałeś/wkleiłeś skryptu, który faktycznie uruchomiłeś?

(C) Niewyjaśnione zjawiska wokół traceback i co rzeczywiste dane wykraczająca było:

"the_script.py", line 40, in <module> 
this_row.append(str(s.cell_value(row,col))) 
UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 5: ordinal not in range(128) 

Po pierwsze, istnieje str() w wierszu szkodliwego kodu, który nie był w uproszczonego scenariusza - prawda nie kopiować/wklejać skryptu, który faktycznie uruchomiłeś? W żadnym wypadku nie powinieneś używać polecenia str w ogóle - nie uzyskasz pełnej precyzji na twoich spławikach; po prostu pozwól, aby moduł CSV je skonwertował.

DRUGI, mówisz "" "Wydaje się, że wartość się odwiesza to" 516-777316 "- tekst w oryginalnym arkuszu Excela to" 516-7773167 "(z 7 na końcu) "" "- trudno sobie wyobrazić, jak 7 znika z końca. Chciałbym użyć coś takiego, aby dowiedzieć się dokładnie, co dane było problematyczne:

try: 
    str_value = str(s.cell_value(row, col)) 
except: 
    print "row=%d col=%d cell_value=%r" % (row, col, s.cell_value(row, col)) 
    raise 

Że% R pozwala uniknąć wpisywania cell_value=%s ... repr(s.cell_value(row, col)) ... The repr() daje jednoznaczną reprezentację danych. Naucz się tego. Użyj tego.

Jak dojechałeś do "516-777316"?

Po trzecie, komunikat o błędzie faktycznie narzeka na znak Unicode u '\ xed' w odsunięciu 5 (tj. Szósty znak). U + 00ED to ŁACIŃSKI MAŁY LIST I I OŚWIADCZENIE, a nie ma nic takiego w "516-7773167"

FOURTHY, lokalizacja błędu wydaje się być ruchomym celem - powiedziałeś w komentarzu do jednego z solutions: "Błąd dotyczący pliku bcw.writerow." Huh?

(D) Dlaczego otrzymałeś ten komunikat o błędzie (przy pomocy str()): str(a_unicode_object) próbuje przekonwertować obiekt unicode na obiekt str, a przy braku jakichkolwiek informacji o kodowaniu używa ascii, ale masz dane inne niż ASCII, więc splat. Zauważ, że twój obiekt tworzy plik CSV zakodowany w utf8, ale twój uproszczony skrypt nie wspomina o utf8 w żadnym miejscu.

(E) "" "s.cell (row, col)) (egscell zamiast s.cell_value) cały dokument zapisuje bezbłędnie, wyjście nie jest szczególnie pożądane (tekst: u'516-7773167 „)«»”

tak się dzieje, ponieważ pisarz CSV jest wywołanie metody swojego obiektu komórki __str__, a to wywołuje <type>:<repr(value)>, które mogą być przydatne do debugowania, ale jak nie powiedzieć tak wielka w pliku csv.

(F) Rozwiązanie Alexa Martellego jest wspaniałe pod tym względem, że powinno się je wykonywać, ale powinieneś przeczytać rozdział dotyczący klasy Cell w dokumentach xlrd: typy komórek to tekst, liczba, boolean, data, błąd, pusty i pusty. Siema masz daty, będziesz chciał je sformatować jako daty, a nie numery, więc nie możesz używać isinstance() (i możesz nie chcieć tak nazywać się wywołaniem funkcji) ... to jest atrybut Cell.ctype i Sheet.cell_type() i Sheet.row_types() metody są dla.

(G) UTF8 nie jest w standardzie Unicode. UTF16LE to nie Unicode. UTF16 to nie Unicode ... a pomysł, że poszczególne struny marnowałyby 2 bajty każdego na BOM UTF16, jest zbyt niedorzeczny, by nawet MS mógł kontemplować :-)

(H) Dalsze czytanie (oprócz dokumentów xlrd):

http://www.joelonsoftware.com/articles/Unicode.html 
http://www.amk.ca/python/howto/unicode 
+1

+1: Dzięki za wspaniałe wyjaśnienie i linki w tle. To uświadomiło mi, że nie mogę przestać uczyć się kodowania dłużej, i doceniam twoje wejście w szczegóły nawet po rozwiązaniu natychmiastowego problemu. – anschauung