2009-07-27 13 views
18

Ich versuche, eine Excel-Tabelle in CSV mit den Python Xlrd und CSV-Modulen zu übersetzen, aber bin auf Kodierungsprobleme hängen. Xlrd erzeugt Ausgabe von Excel in Unicode, und das CSV-Modul benötigt UTF-8.Unicode zu UTF8 für CSV-Dateien - Python über xlrd

Ich bebilderte, dass dies nichts mit dem xlrd-Modul zu tun hat: alles funktioniert gut Ausgabe auf stdout oder andere Ausgaben, die keine spezifische Codierung erfordern.

Das Arbeitsblatt wird als UTF-16-LE codiert, nach book.encoding

Die vereinfachte Version von dem, was ich tue ist:

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) 

Dies erzeugt die folgenden Fehler, etwa 740 Linien in :

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

der Wert scheint bis zu werden, immer aufgehängt ist auf „516-777316“ ist - der Text in dem Originalblatt Excel „516-7773167“ ist (mit einem 7 am Ende)

Ich werde die erste zugeben, dass ich nur eine vage Vorstellung davon, wie Zeichenkodierung funktioniert, so dass die meisten von dem, was ich bisher ausprobiert habe verschiedene Fummelei Permutationen von .encode und .decode auf die s.cell_value(row,col)

sind Wenn jemand eine Lösung vorschlagen könnte, würde ich es begrüßen - noch besser, wenn Sie mir erklären könnten, was nicht funktioniert und warum, damit ich diese Probleme in Zukunft leichter selbst beheben kann.

Vielen Dank im Voraus!

EDIT:

Vielen Dank für die bisher Kommentare.

Wenn ich Benutzer this_row.append(s.cell(row,col)) (z. B. s.cell statt s.cell_value) das gesamte Dokument schreibt ohne Fehler.

Die Ausgabe ist nicht besonders wünschenswert (text:u'516-7773167'), aber es vermeidet den Fehler, obwohl die fehlerhaften Zeichen noch in der Ausgabe sind.

Das lässt mich denken, dass die Herausforderung schließlich in xlrd sein könnte.

Gedanken?

+0

Es wäre nützlich, den gesamten Traceback zu sehen, um zu wissen, wer den Fehler wirft. – Christopher

+0

Nicht viel mehr zu sehen: Datei "the_script.py ", Zeile 40, in this_row.append (str (s.cell_value (row, col))) UnicodeEncodeError: 'ascii' Codec kann das Zeichen nicht in Position 5 'codieren': Ordnungszahl nicht im Bereich (128) – anschauung

+0

Mit "Ausgabe von Excel in Unicode" scheinst du "Ausgabe von Excel in UTF-16" zu bedeuten. Unicode definiert einen Codespace, der durch die verschiedenen Kodierungssysteme, wie UTF-8 oder UTF-16 dargestellt wird. – Svante

Antwort

25

Ich erwarte, dass die cell_value Rückgabewert der Unicode-String ist, dass Sie Probleme ist zu geben (bitte seine drucken type() zu bestätigen, dass), in diesem Fall sollten Sie es lösen können diese eine Zeile durch Änderung:

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

zu:

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

wenn cell_value ist, mehrere verschiedene Arten der Rückkehr, dann müssen Sie codieren, wenn und nur wenn es einen Unicode-String zurückkehrt; so würden Sie diese Zeile in ein paar Zeilen aufgeteilt:

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

Perfekt! Das hat es getan. Ich nehme an, ich hätte nicht erwartet, dass die verschiedenen Werttypen anders behandelt werden müssen. Danke! – anschauung

0

Es scheint zwei Möglichkeiten zu geben. Eine davon ist, dass Sie die Ausgabedatei möglicherweise nicht korrekt geöffnet haben:

"Wenn csvfile ein Dateiobjekt ist, muss es mit dem 'b' Flag auf Plattformen geöffnet werden, wo das einen Unterschied macht." (http://docs.python.org/library/csv.html#module-csv)

Wenn das nicht das Problem ist, dann für Sie eine andere Option ist codecs.EncodedFile (Datei, Eingang [, Ausgang [Fehler]]) zu verwenden, als Wrapper zur Ausgabe Ihrer CSV:

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

Damit können Sie den Dateiobjektfilter von eingehenden UTF16 zu UTF8 haben. Während beide technisch "unicode" sind, ist die Art, wie sie kodieren, sehr unterschiedlich.

Etwas wie folgt aus:

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

kann das Problem für Sie lösen, vorausgesetzt, ich das Problem richtig verstanden, und unter der Annahme, dass der Fehler ausgelöst wird, wenn in die Datei zu schreiben.

+0

Nun, es gab eine andere Fehlermeldung (diesmal bevor Sie etwas in die Datei schreiben): UnicodeDecodeError: 'utf16' Codec kann Byte 0x0a in Position 938 nicht dekodieren: abgeschnittene Daten – anschauung

0

Sieht aus, als hättest du 2 Probleme.

Da ist etwas vermasselt in dieser Zelle - '7' sollte als u'x37 codiert werden 'Ich denke, da es im ASCII-Bereich ist. obwohl

Noch wichtiger ist die Tatsache, dass Sie eine Fehlermeldung, wenn es darum angibt, dass die ascii Codec nicht schlägt die etwas verwendet werden kann, an der Codierung in Unicode - es denkt, dass Sie versuchen, einen Wert zu kodieren 0xed das kann Sie werden nicht in ASCII dargestellt, aber Sie haben gesagt, dass Sie versuchen, es in Unicode darzustellen.

Ich bin nicht schlau genug, um herauszufinden, welche bestimmte Linie das Problem verursacht - wenn Sie Ihre Frage bearbeiten, um mir zu sagen, welche Zeile diese Fehlermeldung verursacht, könnte ich etwas mehr helfen (ich denke, es ist entweder this_row.append(s.cell_value(row,col)) oder bcw.writerow(this_row), aber ich würde Sie gerne bestätigen).

+0

Danke! Der Fehler ist auf bcw.writerow. Alles wird korrekt ausgegeben, wenn ich beispielsweise this_row verwende. Wie ich am besten feststellen kann, gibt es mit der '7' offensichtlich nichts falsches - es wird korrekt ausgegeben (wie u'516-7773167 '), wenn ich auf stdout drucke. – anschauung

+0

Dann sieht es so aus, als erwarte "bcw.writerow" ASCII - sind Sie sicher, dass Sie Ihre Argumente für 'csv.writer' korrekt haben (siehe http://docs.python.org/library/csv.html#csv. Schriftsteller)? Ich bin verwirrt darüber, woher das '0xed' kommt. –

9

Sie suchten nach Erklärungen gefragt, aber einige der Phänomene sind unerklärlich ohne Ihre Hilfe.

(A) Zeichenketten in XLS-Dateien, die ab Excel 97 erstellt wurden, werden in Latin1 kodiert, wenn möglich in UTF16LE. Jede Zeichenfolge trägt eine Flagge, die angibt, welche verwendet wurde. Frühere Excels codierten Strings entsprechend der "Codepage" des Benutzers. In jedem Fall erzeugt xlrd Unicode-Objekte. Die Dateicodierung ist nur dann von Interesse, wenn die XLS-Datei von Software von Drittanbietern erstellt wurde, die entweder die Codepage weglässt oder darüber lügt. Siehe den Unicode-Abschnitt oben in den xlrd-Dokumenten.

(B) unerklärliches Phänomen:

Dieser Code:

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

verursacht die folgenden Fehler mit Python 2.5, 2.6 und 3.1: TypeError: expected at most 2 arguments, got 3 - das ist, was ich die Dokumentation gegeben erwarten würde auf csv.writer; Es erwartet ein dateiähnliches Objekt, gefolgt von (1) nichts (2) einem Dialekt oder (3) einem oder mehreren Formatierungsparametern. Du hast ihm einen Dialekt gegeben, und csv.writer hat kein Codierungsargument, also splat. Welche Version von Python verwendest du? Oder haben Sie das Skript, das Sie tatsächlich ausgeführt haben, nicht kopiert/eingefügt?

(C) Unerklärliche Phänomene um Traceback und was die tatsächliche anstößigen Daten war:

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

ERSTENS, gibt es eine str() in der Linie säumige Code, der nicht in der vereinfachten Skript war - du hast Kopieren Sie nicht das Skript, das Sie tatsächlich ausgeführt haben? In jedem Fall solltest du str nicht generell benutzen - du wirst nicht die volle Präzision auf deinen Floats bekommen; Lass es einfach vom CSV-Modul konvertieren.

SECONDY, Sie sagen "" "Der Wert scheint aufgelegt zu sein ist" 516-777316 "- der Text in der ursprünglichen Excel-Tabelle ist" 516-7773167 "(mit einer 7 am Ende) "" "--- es ist schwer vorstellbar, wie die 7 am Ende verloren geht. Ich würde so etwas wie diese benutze, um genau herauszufinden, was die problematischen Daten waren:

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 

Das% r erspart Sie die Eingabe eines ... cell_value=%s ... repr(s.cell_value(row, col)) die repr() eine eindeutige Darstellung der Daten erzeugt. Lern es. Benutze es.

Wie sind Sie zu "516-777316" gekommen?

DRITTER Weise beschwert sich die Fehlermeldung tatsächlich über ein Unicode-Zeichen u '\ xed' bei Offset 5 (d. H. Das sechste Zeichen). U + 00ED ist LATIN KLEINER BUCHSTABE I MIT AKUTE, und es gibt nichts dergleichen in "516-7773167"

VIERTERS scheint der Fehlerort ein bewegliches Ziel zu sein - Sie sagten in einem Kommentar auf einem der Lösungen: "Der Fehler ist auf bcw.writerow." Hä?

(D) Warum haben Sie diese Fehlermeldung (mit str()): str(a_unicode_object) versucht, das Unicode-Objekt in ein Str-Objekt zu konvertieren, und in Ermangelung jeglicher Codierungsinformationen verwendet ascii, aber Sie haben nicht-ASCII-Daten, so splat. Beachten Sie, dass Ihr Objekt eine csv-Datei erzeugt, die in utf8 codiert ist, aber Ihr vereinfachtes Skript utf8 nirgends erwähnt.

(E) "" "... an Zelle (Zeile, Spalte)) (zB Zelle statt s.cell_value) das gesamte Dokument schreibt ohne Fehler. Die Ausgabe ist nicht besonders wünschenswert (Text: u'516-7773167 ‚)‚‘“

das geschieht, weil der csv Schreiber ist die __str__ Methode der Cell-Objekt aufrufen, und dies erzeugt <type>:<repr(value)>, die für die Fehlersuche nützlich sein können, aber wie Sie sagen, nicht so groß in der cSV-Datei.

(F) Die Lösung von Alex Martelli ist großartig, weil es Sie in Gang gebracht hat, aber Sie sollten den Abschnitt über die Cell-Klasse in den xlrd-Dokumenten lesen: Zellentypen sind Text, Zahl, Boolesch, Datum, Fehler, leer und leer yo Da Sie Daten haben, möchten Sie sie als Datumsangaben formatieren und nicht als Zahlen, so dass Sie isinstance() nicht verwenden können (und den Funktionsaufruf sowieso nicht wollen). Dies ist das Attribut Cell.ctype und Sheet.cell_type() und Sheet.row_types() Methoden sind für.

(G) UTF8 ist kein Unicode. UTF16LE ist kein Unicode. UTF16 ist kein Unicode ... und die Idee, dass einzelne Strings 2 Bytes pro UTF16 BOM verschwenden, ist zu grotesk, um selbst MS zu betrachten :-)

(H) Weiterführende Literatur (abgesehen von den xlrd docs):

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

+1: Danke für die tolle Erklärung und die Hintergrundlinks. Das hat mir klar gemacht, dass ich es nicht vermeiden kann, mich selbst für die Codierung zu sensibilisieren, und ich weiß es zu schätzen, dass du so detailliert ins Detail gehst, auch nachdem das unmittelbare Problem gelöst wurde. – anschauung