UnicodeEncodeError bei Umleitung der Ausgabe in externe Date

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
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hi, ich habe ein Testprogramm geschrieben, das eine XML Datei mit lxml parst und mit print auf der Konsole ausgibt. Läuft die Ausgabe in die Konsole ist alles in Ordnung. Leite ich die Ausgabe in eine Datei um bekomme ich eine Fehlermeldung

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

Die Datei selbst dürfte aber gar keine Unicode Zeichen enthalten. Woher kommt der Unicode Charakter?

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-
from lxml import etree
from array import array

def ReadHex():
    pass

tree = etree.ElementTree()
tree.parse("ATtiny13A.xml")

for node in tree.iter():
    print node.tag, node.text, node.attrib
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Wäre es da nicht angebrachter, uns mal die XML-Datei zu zeigen? ;-) Zumal die Stelle ja netterweise exakt benannt wird.

Im übrigen ist Dein Schnipsel ziemlich überladen mit Dingen, die keine Verwendung finden.

Generell: Was ist ein Unicode-Charakter? Du meinst einfach ein Zeichen, das durch die ASCII-Kodierung nicht dargestellt werden kann.

Auf welchem System arbeitest Du denn? Also OS / ggf. Shell?

Hast Du Dir mal im wiki die Seite über Unicode und Umlaute durchgelesen? Zudem kannst Du im wiki mal nach "München" suchen und Dir die Folien von Leonidas vom 13. o. 14. Usertreffen in München angucken. Imho ist beides sehr zu empfehlen, um ein grundlegendes Verständnis für den Umgang mit Unicode zu bekommen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Die XML Datei ist ziemlich lang und ich darf sie nicht weitergeben. Ich kann vielleicht die ersten paar Zeilen posten

Code: Alles auswählen

<?xml version="1.0"?>
<AVRPART>
  <MODULE_LIST>[INTERRUPT_VECTOR:PACKAGE:FUSE:CORE:PROGRAMMING:LOCKBIT:MEMORY:ADMIN:IO_MODULE:ICE_SETTINGS]</MODULE_LIST>
  <INTERRUPT_VECTOR>
    <NMB_VECTORS>10</NMB_VECTORS>
    <VECTOR1>
      <PROGRAM_ADDRESS>$000</PROGRAM_ADDRESS>
      <SOURCE>RESET</SOURCE>
      <DEFINITION>External Reset, Power-on Reset and Watchdog Reset</DEFINITION>
    </VECTOR1>
System ist übrigens Ubuntu 10.10
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Nochmal als Ergänzung: Das Problem tritt nur auf, wenn ich node.text eingefügt habe. Ohne kann ich problemlos umleiten.

An der angegebenen Position ändert sich auch nichts, wenn ich einige der Zeilen lösche.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Sorry das ich nochmal poste. Ich hab den Schuldigen gefunden

Code: Alles auswählen

<TEXT>The 8-bit Timer/Counter0 can select clock source from CK, prescaled CK, or an external pin. In addition it can be stopped as described in &#x201C;Timer/Counter0 Control Register - TCCR0&#x201D; on page 35.....</TEXT>
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ja, aber du hast das Problem offenbar immer noch nicht verstanden. Du hast Unicode-Objekte, die du irgendwie ausgibst. Da Python "rät" dass du diese Objekte als ASCII enkodieren willst versucht es diese Objekte eben als ASCII auszugeben was scheitert sobald ein Zeichen dass nicht in ASCII enkodierbar ist, vorkommt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Nein, mein anfängliches Problem war, dass ich nicht erklären konnte, woher die Zeichen kamen. Wenn ich mit print den Text in der Konsole ausgebe werden die Sonderzeichen korrekt interpretiert. Mein derzeitiges Problem ist, dass ich nicht weiß von wem.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
BlackJack

@burli: Das kann Dir im Grunde egal sein, da es alles andere als garantiert ist, musst Du Dich sowieso darum kümmern.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

burli hat geschrieben:Nein, mein anfängliches Problem war, dass ich nicht erklären konnte, woher die Zeichen kamen. Wenn ich mit print den Text in der Konsole ausgebe werden die Sonderzeichen korrekt interpretiert. Mein derzeitiges Problem ist, dass ich nicht weiß von wem.
Interpretiert werden Zeichen immer von der darstellenden Umgebung; das kann ein Texteditor sein, eine Shell oder sonst was. Dein Problem liegt bei Python selber.

Zur Verdeutlichung:

Code: Alles auswählen

In [1]: import sys

In [2]: sys.getdefaultencoding()
Out[2]: 'ascii'

In [3]: sys.getfilesystemencoding()
Out[3]: 'UTF-8'
Ich benutze übrigens im Moment auch Ubuntu 10.10, also sollte es bei Dir analog aussehen. Das defaultencoding ist also "ascii". Python wird also immer versuchen, einen Unicode-String in einen Bytestring mit der Codierung ASCII umzuwandeln. Sobald Du ein Zeichen den möglichen Codierungsraum von ASCII übersteigt, bekommst Du die Exception.

So weit sollte es klar sein.

Nun kommt ein Verhalten, das mir im Moment auch nicht komplett einleuchtet.

Beim normalen print in eine Shell hinein, scheint Python das Filesystemencoding zu benutzen. In diesem Falle also utf-8, weswegen die Sonderzeichen ohne Probleme encodiert werden können.

Sobald Du eine Umleitung in eine Datei vornimmst, scheint Python das Defaultencoding zu benutzen, was dann in einem Fehler mündet.

Wieso Python das macht bzw. erkennen kann, ist mir im Moment auch schleierhaft!

Allerdings kannst Du das ja leicht umgehen, indem Du immer explizit bei IO-Operationen das Encoding angibst. Imho eh der beste Weg, weil Du Dich dann nicht an die "Willkür" des zugrunde liegenden OS bindest.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

if __name__ == "__main__":
    print sys.getdefaultencoding()
    print u"Hallöle".encode("utf-8")
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Hyperion: Die Information kommt hierher:

Code: Alles auswählen

In [419]: sys.stdout.encoding
Out[419]: 'UTF-8'

In [420]: sys.stdout.isatty()
Out[420]: True
Wenn eine Datei mit einem Terminal verbunden ist, kann Python herausfinden welche Kodierung dort erwartet wird und ``print`` nutzt diese Information. In Fällen wo das nicht geht, ist das `encoding`-Attribut `None` und ``print`` fällt auf `sys.getdefaultencoding()` zurück, also 'ascii'.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Ok, danke. Damit komme ich denke ich weiter
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Ich bin immer noch verwirrt.

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function

tree = etree.parse("/home/burli/Ubuntu One/Python/gedit/PDFConvert/pdf/%s" % xmlfile)
part = tree.find(".//PART_NAME").text
print(type(part.encode("utf-8")))
Trotz allem bekomme ich hier als Typ ein <type 'str'>. Ich werde nicht schlau daraus. Was gibt es denn da noch zu beachten?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

burli hat geschrieben:Ich bin immer noch verwirrt.

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function

tree = etree.parse("/home/burli/Ubuntu One/Python/gedit/PDFConvert/pdf/%s" % xmlfile)
part = tree.find(".//PART_NAME").text
print(type(part.encode("utf-8")))
Trotz allem bekomme ich hier als Typ ein <type 'str'>. Ich werde nicht schlau daraus. Was gibt es denn da noch zu beachten?
Hast Du Dir denn mal die von mir angegebenen Ressourcen wirklich angeguckt? Dann sollte Dir spätestens dann klar sein, dass "encode" vor allem auf einem Unicode-Objekt angewendet wird, um dieses in einen (codierten) Byte-String zu wandeln. Ergo ist der Typ nach einem encode auch "str" (in Python2 zumindest).

Zudem mag es sein, dass Du aus dem etree-Parser auch Bytestrings bekommt, sofern diese im ASCII-Bereich liegen. (Das macht aber ja nichts, weil diese durch ein encode nicht "zerstört" werden).

Kannst Du ja mal einfach testen, indem Du Dir ein einfaches XML-Dokument anlegst und einmal einen Umlaut in einem Element unterbringst und einmal nur normale ASCII-Zeichen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@burli: Was genau verwirrt Dich denn? `encode()` liefert einen `str`. Genau das will man hier doch auch. Du brauchst die Bytekette ja um sie zu rausschreiben zu können. Wenn `encode('utf-8')` wieder `unicode` als Typ liefern würde, wie sollte *das* denn dann bitte aussehen!? `unicode` sind Zeichen und `str` sind Bytewerte. Mit der "Aussenwelt" kann man nur Bytewerte austauschen, also muss man die Zeichen irgendwie in Bytewerte umwandeln -- das geht mit `encode()` und einer Angabe *wie* Zeichen in Bytewerte umgesetzt werden sollen. Zum Beispiel 'utf-8', was alle Unicode-Zeichen in Bytes umwandeln kann, oder zum Beispiel 'iso-8859-1', was nur eine Untermenge von weniger als 256 Zeichen in Bytes umwandeln kann.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

ok, ich hab mich nicht klar genug ausgedrückt. Die Variable "part" ist auch ohne .encode("utf-8") bereits ein Bytestring, ich möchte aber einen Unicode String und ich dachte, mit encode wird daraus ein Unicode String. Die Variable soll anschließend in eine Datenbank geschrieben werden

Code: Alles auswählen

from elixir import *

metadata.bind = "sqlite:///avrpd.sqlite"
#metadata.bind.echo = True

class device(Entity):
	part_name = Field(Unicode)
Ich könnte hier zwar auch String verwenden, aber ich dachte, ich denke etwas voraus und verwende schon jetzt wo immer möglich Unicode, um später bei Python 3 nicht nachbessern zu müssen
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann brauchst du nicht encode, sondern decode! ;-)

(Wird auch in den von mir angegebenen Ressourcen beschrieben!)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Kopf -> Tisch *au*

Oh man, sorry. Danke. Mal wieder völlig neben der Spur :roll:
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Antworten