imaplib: Problem mit Zeichencodierung

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

imaplib: Problem mit Zeichencodierung

Beitragvon burli » Mittwoch 20. Februar 2008, 11:20

Hi, ich versuche mit Hilfe der imaplib Emails zu bearbeiten. Das funktioniert soweit. Nur die Zeichencodierung passt nicht. Die Mails liegen im ISO 8859 Standard vor, aber Python verarbeitet das anscheinend als ASCII (also nur 0-127).

Jetzt weiß ich nicht ob man imaplib.fetch() dazu überreden kann das als ISO 8859-15 zu behandeln oder gleich in Unicode zu konvertieren. Geht das irgendwie?

Code: Alles auswählen

import imaplib

im=imaplib.IMAP4('imap.server.de')

im.login('user', 'passwd')
im.select("INBOX")

ok, result = im.search(None, 'UNSEEN')

for mail in result[0].split():
    print mail
    typ, daten = im.fetch(mail,  "(BODY[TEXT])")
    print "%s\n+++\n" % daten[0][1]
im.close()
im.logout()
BlackJack

Beitragvon BlackJack » Mittwoch 20. Februar 2008, 11:55

Mails sind normalerweise nur ASCII. Alles ausserhalb von 0-127 wird in der Regel irgendwie kodiert. Wie sehen denn zum Beispiel Umlaute bei Dir aus?
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Beitragvon burli » Mittwoch 20. Februar 2008, 11:58

diese invertierten ? in einer Raute
BlackJack

Beitragvon BlackJack » Mittwoch 20. Februar 2008, 12:36

Äh, okay wie sehen die Daten *wirklich* aus. Bis Du zu den � kommst, werden die Daten ja noch mindestens einmal durch irgendein Programm interpretiert, dass die Bytes als Zeichen anzeigt.

Also was ergibt: ``print type(s)`` und ``print repr(s)`` für die Daten?
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Beitragvon burli » Mittwoch 20. Februar 2008, 12:49

Also, das komplette Programm sieht du oben. Die relevanten Daten stehen in "daten". daten ist eine List.

daten[0] ist ein Tuple, daten[1] ist ein String. Ich hab jetzt mal folgendes gemacht

Code: Alles auswählen

for mail in result[0].split():
    typ, daten = im.fetch(mail,  "(BODY[TEXT])")
    print type(daten[0])
    print "----"
    print daten[0]
    print "----"
    print type(daten[1])
    print "----"
    print daten[1]
    print "----"
    print daten[0][1]


Als ergebnis bekomme ich
<type 'tuple'>
----
('107 (FLAGS (\\Seen) BODY[TEXT] {18}', 'Test Umlaut \xe4\xf6\xfc\xdf\r\n')
----
<type 'str'>
----
)
----
Test Umlaut ���


EDIT: Ok, ich bin gerade auf etwas reingefallen. mit daten[0][1] greift man auf den String in dem Tuple zu, also

Test Umlaut \xe4\xf6\xfc\xdf\r\n

Die Umlaute sind, wie du schon vermutet hast, verschlüsselt. Die muss ich jetzt nur noch entschlüsseln. Aber wie?
BlackJack

Beitragvon BlackJack » Mittwoch 20. Februar 2008, 13:14

Das sind auf jeden Fall schon mal Bytes ausserhalb von ASCII. Also die dort scheinen als iso8859-1 kodiert zu sein.

Code: Alles auswählen

In [467]: '\xe4\xf6\xfc\xdf'.decode('iso8859-15')
Out[467]: u'\xe4\xf6\xfc\xdf'

In [468]: print '\xe4\xf6\xfc\xdf'.decode('iso8859-15')
äöüß
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Beitragvon burli » Mittwoch 20. Februar 2008, 13:28

Sowas ähnliches hab ich auch schon probiert.

Code: Alles auswählen

    print daten[0][1].decode('iso8859-15')


Dabei kommt aber folgende Fehlermeldung

Traceback (most recent call last):
File "/workspace/imap/src/imap/__init__.py", line 17, in <module>
print daten[0][1].decode('iso8859-15')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 12-15: ordinal not in range(128)


Bei Deiner Zeile kommt im übrigen die gleiche Meldung wenn ich die in das Programm einfüge. Wenn ich die dagegen in einer Python Shell wie IDLE eingebe funktioniert es. Hab nur keine Ahnung warum
BlackJack

Beitragvon BlackJack » Mittwoch 20. Februar 2008, 13:37

Die Ausnahme kommt nicht vom `decode()`, wie man schön sehen kann ist das ein `UnicodeEncodeError`! Das ``print`` muss ja nach aussen wieder Bytes rausgeben und versucht deshalb das Unicode-Objekt wieder in solche um zu wandeln. Da Python an der Stelle nicht weiss, welche Kodierung die Anwendung auf `stdout` erwartet, wird's mit ASCII versucht. Das geht natürlich mit Umlauten nicht.

Setz Dich mal mit Unicode auseinander! Unicode kann nur innerhalb eines Programms existieren. Daten von "draussen" und nach "draussen" sind immer irgendwie als Bytes kodiert. Und das musst *Du* machen, weil man den Daten nicht ansehen kann, *wie* sie kodiert sind. Das muss der Programmierer wissen.
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Beitragvon burli » Mittwoch 20. Februar 2008, 13:44

Ah, ok. Dh wenn ich im Programm zb

Code: Alles auswählen

test=daten[0][1].decode('iso8859-15')


schreibe ist test ein Unicode String in dem die Umlaute von ISO8859 in Unicode umgewandelt wurden. print kann den String aber trotz allem nicht ausgeben weil print nicht weiß wie die Sonderzeichen ausgegeben werden sollen. In der Shell funktioniert es wohl deshalb weil die Shell selbst komplett als Unicode läuft?
BlackJack

Beitragvon BlackJack » Mittwoch 20. Februar 2008, 13:58

Das funktioniert immer dann, wenn `sys.stdout.encoding` nicht `None` ist und stimmt. Das ist in IDLE der Fall und unter Linux in den üblichen Terminals auch.
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Beitragvon burli » Mittwoch 20. Februar 2008, 14:46

Ok, ich hab zwar ein paar Sachen gefunden, aber wie ich sys.stdout.encoding umstelle noch nicht.

Code: Alles auswählen

print sys.getfilesystemencoding()
print sys.getdefaultencoding()
print sys.stdout.encoding


ergibt

UTF-8
ascii
None


Zumindest temporär funktioniert es jetzt

Code: Alles auswählen

test = '\xe4\xf6\xfc\xdf'.decode('iso8859-15')
print test.encode('utf-8')


Aber vielleicht kannst du mir noch verraten wie ich sys.stdout.encoding setzen kann? Dazu hab ich noch nichts finden können. Versuche mit sys.setdefaultencoding='utf-8' oder ähnlichem haben noch nicht gefruchtet
BlackJack

Beitragvon BlackJack » Mittwoch 20. Februar 2008, 15:00

Das kann man gar nicht setzen. Du kannst die Sachen im `codecs`-Modul benutzen, um die Standardausgabe in ein entsprechendes Objekt zu verpacken, damit transparent kodiert wird.
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Beitragvon burli » Mittwoch 20. Februar 2008, 15:07

Mhm, ok, das krieg ich jetzt beim schnellen Überfliegen nicht auf die Reihe. Das muss ich in einer ruhigen Minute mal durcharbeiten. Aber zumindest kann ich jetzt weitermachen.

Ich danke dir recht herzlich für die Hilfe

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot]