Unicode (verständnis)Problem

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
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo

Bei nachstehendem Skript funktioniert die Unicode Umwandlung irgendwie nicht ganz.

Code: Alles auswählen

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

import smtplib as s
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr

un = "xxxxxx"
pw = "xxxxxx"

print u"Bitte Name des Empfängers eingeben:"
name = raw_input().encode(' utf-8') 

print u"Bitte Mailadresse eingeben:"
to = raw_input().encode(' utf-8') 

to = formataddr((str(Header(u"%s" % name, ' utf-8')), "%s" % to))

print u"Bitte Name des Absenders eingeben:"
abName = raw_input().encode(' utf-8') 

print u"Bitte Mailadresse eingeben:"
me = raw_input().encode(' utf-8') 
me = formataddr((str(Header(u'%s' % abName, ' utf-8')), "%s" % me))

print u"Bitte Betreff eingeben:"
subject = raw_input().encode(' utf-8') 
subject = str(Header(u'%s' % subject, ' utf-8'))


print u"Bitte Text eingeben:"

nachricht = raw_input().encode(' utf-8') 

text = u'%s' % nachricht


msg = MIMEMultipart('alternative')
msg.set_charset('utf8')
msg['Subject'] = subject
msg['From'] = me
msg['To'] = to


part1 = MIMEText(text.encode('utf-8'), _charset='utf-8')
msg.attach(part1)

obj = s.SMTP("xxxx.xxxxx.xx:587")
obj.starttls()
obj.login(un, pw)

obj.sendmail(me, to, msg.as_string())
dir(obj)
obj.quit()
print 'Mail erfolgreich versendet'

Wenn ich einen Namen mit Ü, Ä é etc. eingebe, dann erscheint folgende Meldung:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf6 in position 1: ordinal not in range(128)
Aber wenn ich alles (also utf-8) durch iso-8859-1 ersetze, dann kommt die Mail problemlos an und alle Umlaute werden auch korrekt angezeigt.

Was muss ich tun, wenn ich

Code: Alles auswählen

#-*- coding: utf-8 -*-
verwenden will und nicht

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-
bzw. wo hab ich was verwechselt oder nicht kapiert?

mfg
BlackJack

@lackschuh: Der Kodierungskommentar gilt einzig und alleine für den *Quelltext* des Moduls und sollte der Kodierung entsprechen, in der der Quelltext auch tatsächlich gespeichert wurde.

`raw_input()` gibt eine Zeichenkette (`str`) zurück und da macht es keinen Sinn `encode()` mit einer Zeichenkodierung aufzurufen. Die ist ja bereits irgendwie kodiert. Und wenn man das trotzdem macht, versucht Python das `str`-Objekt erst als ASCII in ein `unicode`-Objekt zu dekodieren um *das* dann mit der angegebenen Kodierung wieder zu einem `str` zu kodieren. Das ist sinnfrei und führt bei Zeichen ausserhalb von ASCII genau zu der gezeigten Ausnahme.

Die diversen ``'%s' % obj`` und ``u'%s' % obj`` sind auch komisch bis unsinnig. Wenn man das *wirklich* machen will, dann wären `str()`- und `unicode()`-Aufrufe zumindest schon einmal verständlicher beim Lesen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ich versuche mich mal an einer Antwort...

'raw_input()' liefert Dir einen string, der mit Deinem Systemencoding kodiert ist. Das heißt, wenn Dein System mit utf8 kodiert, dann bekommst Du

Code: Alles auswählen

>>> raw_input()
ä
'\xc3\xa4' #Das ist ein utf-8 kodierter string
Läuft Dein System unter iso8859 (latin), dann schaut es so aus:

Code: Alles auswählen

>>> raw_input()
ä
'\xe4' #Das ist ein iso-8859-1 kodierter string
Deinem Beitrag entnehme ich, dass Dein System utf8 verwendet. Wie BlackJack ja bereits geschrieben hat ist der string, den Dir 'raw_input()' liefert bereits codiert, in Deinem Fall als utf8. Deshalb bekommst Du einen Fehler geworfen, da Du einen utf8-kodierten string nochmal in einen utf8-string kodieren möchtest.

Es empfiehlt sich, programmintern mit unkodierten strings, sprich unicode-strings zu arbeiten. Einen solchen string erhälts Du z. B. so:

Code: Alles auswählen

>>> import sys
>>> ENCODING = sys.getfilesystemencoding()
>>> raw_input().decode(ENCODING)
ä
u'\xe4'
Diesen unicode-string kannst Du dann wieder in ein encoding wie utf8 oder iso8859 kodieren. Also:

1. 'raw_input()' liefert Dir einen bereits kodierten string.
2. Die Kodierung, die Du in Deinem Modul über '#-*- coding: irgendwas -*-' angibst hat damit nichts zu tun
3. Es empfiehlt sich, programmintern mit unicode-strings zu arbeiten
4. unicode ist KEIN encoding!

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

BlackJack hat geschrieben:`raw_input()` gibt eine Zeichenkette (`str`) zurück und da macht es keinen Sinn `encode()` mit einer Zeichenkodierung aufzurufen.
Danke für die Infos. Ich hab mal die ganzen `encode()` weggemacht und es geht auch nun ohne. Das erste Problem lag an Eclipse. Hab Eclipse nun so eingestellt, dass die Konsole die Umlaute auch ohne .encode() korrekt anzeigt.
BlackJack hat geschrieben:Die diversen ``'%s' % obj`` und ``u'%s' % obj`` sind auch komisch bis unsinnig. Wenn man das *wirklich* machen will, dann wären `str()`- und `unicode()`-Aufrufe zumindest schon einmal verständlicher beim Lesen.
Was wäre eine bessere Lösung, wenn ich noch am Anfang die Daten per Konsole eingeben will?

mfg
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

lackschuh hat geschrieben:Das erste Problem lag an Eclipse. Hab Eclipse nun so eingestellt, dass die Konsole die Umlaute auch ohne .encode() korrekt anzeigt.
Zu Eclipse kann ich nichts sagen... ich hatte aber mit IPython bis zur Version 0.11 (seit 0.12 klappt's) auch Momente, in denen ich kurz vor dem Wahnsinn stand, weil einfach nicht korrekt kodiert wurde...
Ich kann Dir nur empfehlen, gerade beim Testen der richtigen Anzeige auf die pythoneigene Konsole zurückzugreifen.
lackschuh hat geschrieben:Ich hab mal die ganzen `encode()` weggemacht und es geht auch nun ohne.
Wobei das nichts mit Eclipse zu tun hat. Die Erklärung dafür hast Du ja schon bekommen.
lackschuh hat geschrieben:
BlackJack hat geschrieben:Die diversen ``'%s' % obj`` und ``u'%s' % obj`` sind auch komisch bis unsinnig. Wenn man das *wirklich* machen will, dann wären `str()`- und `unicode()`-Aufrufe zumindest schon einmal verständlicher beim Lesen.
Was wäre eine bessere Lösung, wenn ich noch am Anfang die Daten per Konsole eingeben will?
BlackJack hat es ja schon angedeutet: Warum möchtest Du das denn überhaupt machen? Was möchtest Du damit erreichen?
Nochmal:
- 'raw_input()' liefert je nach Systemencoding einen kodierten string (meistens utf8 oder ein iso8859 Encoding)
- Wenn Du einen unicode-string brauchst, erhältst Du den über 'unicode()' oder die 'decode()'-Methode des jeweiligen strings

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

@mutetella: ``sys.getfilesystemencoding()`` sagt dir nicht das Encoding von Stdin, daher kann das klappen, muss aber nicht. Da ist es schon sinnvoller zu gucken was das Locale ist, aber selbst das ist nicht unbedingt Zielführend.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Leonidas: Ok, dann also 'locale.getpreferredencoding()', oder?
Ich dachte halt, nachdem 'stdin' ja auch 'irgendwie' 'ne Datei ist kann mit größerer Wahrscheinlichkeit davon ausgegangen werden, dass strings von 'stdin' auch im filesystemencoding vorliegen.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Und wenn man Glueck hat, hilft das

Code: Alles auswählen

In [1]: import sys

In [2]: sys.stdin.encoding
Out[2]: 'UTF-8
Std* sind zwar Dateien, aber befinden sich nicht im Dateisystem, zumindest nicht in einem "normalen". ;)
deets

mutetella hat geschrieben:@Leonidas: Ok, dann also 'locale.getpreferredencoding()', oder?
Ich dachte halt, nachdem 'stdin' ja auch 'irgendwie' 'ne Datei ist kann mit größerer Wahrscheinlichkeit davon ausgegangen werden, dass strings von 'stdin' auch im filesystemencoding vorliegen.
Das hat doch nichts miteinander zu tun. Das eine gilt fuer die *Namen* einer Datei, das andere fuer deren Inhalt - und ist zB von Terminal-Settings abhaengig, die sich jederzeit aendern koennen. Das der Filenamen nicht so.

Diez
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@deets
Hab's auch gerade gelesen... :oops:

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

mutetella hat geschrieben:Ich dachte halt, nachdem 'stdin' ja auch 'irgendwie' 'ne Datei ist kann mit größerer Wahrscheinlichkeit davon ausgegangen werden, dass strings von 'stdin' auch im filesystemencoding vorliegen.
Das mit dem Filesystem-Encoding ist eh eine großer Quatsch. In NTFS sind ist das Encoding der Dateien UTF-16, in FAT kanns alles mögliche sein und in Unix kanns alles mögliche sein bunt gemischt, weil in Unix-Dateisystemen die Dateinamen binär sind, jedem User und App steht frei seine Dateinamen in UTF-8 bis -32, Big5, EUC-JP, KOI-8R oder was auch immer zu kodieren. Und in Mac OS X ändern sie die Art wie sie Normalisieren von Release zu Release. Das ganze war ja auch in Python 3 so ein Problem mit dem Öffnen von Dateien.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten