VErarbeiten von CSV in UTF-8: UnicodeEncodeError

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Hallo,

ich bastle jetzt schon seit heute morgen an einem Problem, aber ich kriegs irgendwie nicht gebacken.....

Ich hab eine CSV-Datei aus 3 Spalten, getrennt durch Semicolon, in UTF-8. Die Felder können Umlaute oder sonstiges enthalten (sollte ja bei UTF-8 kein Ding sein).
Das ganze unter Python 3.5.3.

Die CSV hat momentan folgenden Inhalt:

Code: Alles auswählen

zeile 1;aeiouäöü;ÖÖÖÖÄÄÄÄÄÜÜÜÜ;
Zeile 2;ÖÖÖÖÄÄÄÄÄÜÜÜÜ;aeiouäöü;
ICh mache das wie folgt auf:

Code: Alles auswählen

>>> with open("test.csv",'rU',encoding='utf-8') as csvfile:
...     xxx=csv.reader(csvfile, delimiter=';', quotechar='"')
...     for row in xxx:
...             print(row[0])
...             print(row[1])
... 
zeile 1
Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 5-7: ordinal not in range(128)
Tja, jetzt die Preisfrage: Warum? Da ist nichts drin, was nicht durch UTF-8 abgedeckt wäre....

Hab ich da irgendwo ein Verständnisproblem?

Gruß

Daniel
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du hast ein Verständnisproblem. Schau mal genau auf die Zeilennummer, und wie GENAU die Ausnahme heißt.
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Hallo,

dann steh ich auf dem Schlauch. Stolpert er über das Leerzeichen?
Das hab ich auch mal raus und durch nen Unterstrich ersetzt....

Code: Alles auswählen

Python 3.5.3 (default, Sep 27 2018, 17:25:39) 
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import csv
>>> with open("test.csv",'rU') as csvfile:
...     xxx=csv.reader(csvfile, delimiter=';', quotechar='"')
...     for row in xxx:
...             print(row[0])
...             print(row[1])
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "/usr/lib/python3.5/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 13: ordinal not in range(128)

Gruß

Daniel
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dein Problem entsteht beim PRINT. Nicht beim einlesen. Und du hast ein ENCODING-Problem, also der Versuch, unicode (das du im Python-Interpreter hast) auszugeben. Dazu muss das ENKODIERT werden, und warum auch immer deine Terminal oder IDE-Settings das verbocken, aber da will Python ascii verwenden. Du musst also die Einstellungen deiner Umgebung da so aendern, dass Python inferieren kann, welches Encoding zB dein Terminal verwendet. Oder wie auch immer deine IDE das macht. Oder (testweise mindestens) mal einfach "row[0].encode('utf-8')" benutzen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: meine Anmerkung bezog sich auf deinen ersten Post. Im zweiten Post hast du ja das utf-8 entfernt aus dem einlesen, das ist dan DEKODIEREN, und geht natuerlich in die Hose.
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Jo, danke, Groschen gefallen.

Ich hab das ganze im Xterm aufgeufen, das kann kein UTF-8. Im Uxterm gehts.
Das stellt mich dann vor die Aufgabe das ganze zur jeweiligen Umgebung passend kodiert auszugeben....

Danke soweit mal.


Gruß

Daniel
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Fuer print/sys.stdout sollte Python das normalerweise selbst rausfinden. Wenn du selber Dateien speicherst, dann ist das immer deine Aufgabe, ja. Nur du kannst wissen, welches Format zur Weitergabe geeignet ist.
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Die Datei wird nur eingelesen und Teile davon wieder auf Stdout ausgegeben.
Wie krieg ich das Encoding der Umgebung am besten raus? sys.stdout.getencoding?
Und dann vor der Ausgabne mit irgendwas.encode(sys.stdout.getencoding)?
Oder gibts eleganteres?

Gruß

Daniel
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du print benutzt, dann wird das doch implizit fuer dich gemacht. Was willst du da also selbst tun?
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Ich lese UTF-8 ein und gebs mit Print aus. Nun ruft halt nicht jedes das Programm in ner UTF-8-Umgebung.
Somit brichts dann mit obigem Fehler ab.
Oder was kann ich anders machen damit mir das nicht passiert?
Wenn da ein paar unlesbare Zeichen stehen ists egal, es sollte nur nicht abbrechen.

Gruß

Daniel
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die encode()-Methode hat einen error-Parameter mit dem man das Verhalten im Falle von nicht darstellbaren Zeichen bestimmen kann:

Code: Alles auswählen

'späm'.encode('ascii', errors='replace')
Dies kannst du jetzt mit dem Encoding des Ausgabe-Streams kombinieren:

Code: Alles auswählen

text.encode(sys.stdout.encoding, errors='replace')
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Danke, jetzt bricht er schon mal nicht ab.
Aber wie krieg ich das "b" noch vor der Ausgabe weg?

Code: Alles auswählen

b'zeile_1'
b'aeiou???'
b'Zeile_2'
b'?????????????'
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das wird etwas komplizierter, sollte aber so klappen:

Code: Alles auswählen

import io
import sys

stdout = io.TextIOWrapper(sys.stdout.buffer, encoding=sys.stdout.encoding, errors='replace')
print(text, file=stdout)
Möglicherweise gibt es eine schönere Lösung, aber mir fiel nur diese ein.

In Python 3.7 wäre es ein simples:

Code: Alles auswählen

sys.stdout.reconfigure(errors='replace')
... aber das nutzt da ja nicht und bringt dir somit nichts.
Daniel26
User
Beiträge: 26
Registriert: Freitag 2. Juni 2017, 09:29

Danke, werds versuchen.

Gruß

Daniel
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Oder du schreibst die Bytes direkt in stdout:

Code: Alles auswählen

encoded = text.encode(sys.stdout.encoding, errors='replace')
sys.stdout.buffer.write(encoded + b'\n')
Antworten