String zusammensetzen

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.
peddy
User
Beiträge: 121
Registriert: Montag 30. Juni 2008, 13:51

Hallo,

ich möchte den Text für eine Email zusammensetzen und stelle mich dabei gerade etwas doof an :-( Ziel soll es sein die drei Personen untereinander stehen zu haben. Also so:
Hallo,

diese neuen Mitarbeiter wurden gefunden:
- Ute Schneider
- Peter Muster
- Manuela Becker
Ich dachte ich könnte mit \n ein Newline einbauen, aber das klappt im unteren Beispiel nicht.

Code: Alles auswählen

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

liste = [(372, u'Schneider', u'Ute', u'201303'),
    (470, u'Muster', u'Peter', u'201303'),
    (526, u'Becker', u'Manuela', u'201303')]

def build_email_text(liste):

    text = ""
    for item in liste:
        text += `item[2] + " " + item[1] + "\n"`

    email_text = """Hallo,

    diese neuen Mitarbeiter wurden gefunden:
    {}""".format(text)

    return email_text

if __name__ == '__main__':
    print build_email_text(liste)
xeike
User
Beiträge: 83
Registriert: Donnerstag 28. Februar 2013, 09:58

Wie wäre es mit:

Code: Alles auswählen

text += "  -" + item[2] + " " + item[1] + "\n"
(also ohne Ticks)

Xe
BlackJack

@peddy: Wie bist Du denn bitte auf die Backticks gekommen? Das ist eine veraltete Möglichkeit die `repr()`-Funktion zu verwenden, und diese Darstellung ist ja gerade explizit nicht für Endbenutzer sondern für Programmierer zur Fehlersuche gedacht.

Code: Alles auswählen

personen = [
    (372, u'Schneider', u'Ute', u'201303'),
    (470, u'Muster', u'Peter', u'201303'),
    (526, u'Becker', u'Manuela', u'201303'),
]

def build_email_text(liste):
    return (
        'Hallo,\n'
        '\n'
        'diese neuen Mitarbeiter wurden gefunden:\n'
        + '\n'.join(
            '{0} {1}'.format(vorname, name) for _, name, vorname, _ in personen
        )
    )


if __name__ == '__main__':
    print build_email_text(personen)
peddy
User
Beiträge: 121
Registriert: Montag 30. Juni 2008, 13:51

BlackJack hat geschrieben:Wie bist Du denn bitte auf die Backticks gekommen? Das ist eine veraltete Möglichkeit die `repr()`-Funktion zu verwenden, und diese Darstellung ist ja gerade explizit nicht für Endbenutzer sondern für Programmierer zur Fehlersuche gedacht.
Wenn ich das noch wüsste. Ich habe so viel probiert, dass ich mich nicht mehr genau erinnern kann.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

oder:

Code: Alles auswählen

personen = [
    (372, u'Schneider', u'Ute', u'201303'),
    (470, u'Muster', u'Peter', u'201303'),
    (526, u'Becker', u'Manuela', u'201303'),
]

def build_email_text(personen):
    return """Hallo,

diese neuen Mitarbeiter wurden gefunden:
{}
""".format('\n'.join(map('{0[2]} {0[1]}'.format,personen))

if __name__ == '__main__':
    print build_email_text(personen)
peddy
User
Beiträge: 121
Registriert: Montag 30. Juni 2008, 13:51

Jetzt habe ich ein weiteres Problem festgestellt. Die Daten kommen eigentlich von Sqlite, daher werden sie als Unicode geliefert. Habe ich einen Namen mit Umlaut kommt es zur Fehlermeldung.
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 1: ordinal not in range(128)

Code: Alles auswählen

personen = [
    (372, u'K\xf6ne', u'Ute', u'201303'),
    (470, u'Muster', u'Peter', u'201303'),
    (526, u'Becker', u'Manuela', u'201303'),
]
Entweder muss ich den Unicode in UTF-8 umwandeln oder dieser Abschnitt muss mit Unicode umgehen können:

Code: Alles auswählen

def build_email_text(liste):
    return (
        'Hallo,\n'
        '\n'
        'diese neuen Mitarbeiter wurden gefunden:\n'
        + '\n'.join(
            '{0} {1}'.format(vorname, name) for _, name, vorname, _ in personen
        )
    )
Im Moment eine harte Nuss für mich. Leider kann man Listen und Tuples nicht Encodieren. Wo würdet ihr am ehesten ansetzen?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

wenn der Funktionsname richtig gewählt wurde, handelt es sich ja um einen Email-Text.
Das richtige vorgehen ist also, beim Senden der email das passende Encoding (z.B. utf-8) einzustellen und den Email-Text in diesem Schritt mit .encode auch erst umzuwandeln.
Die interne Verarbeitung sollte von Anfang bis Ende auf Unicode-Strings basieren und niemals mit codierten Byte-Strings gemischt werden.
peddy
User
Beiträge: 121
Registriert: Montag 30. Juni 2008, 13:51

Ok, durchgehend mit Unicode arbeiten und erst am Ende umwandeln klingt vernünftig, leider macht mir die Zeile mit dem join einen Strich durch die Rechnung.
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 1: ordinal not in range(128)

Code: Alles auswählen

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

personen = [
    (372, u'K\xf6ne', u'Ute', u'201303'),
    (470, u'Muster', u'Peter', u'201303'),
    (526, u'Becker', u'Manuela', u'201303'),
]

print '\n'.join('{0} {1}'.format(vorname, name) for _, name, vorname, _ in personen)
Ich habe den Verdacht, dass in den Variablen vorname und name kein Unicode mehr ankommt. Ich habe auch schon mal was wie unicode(name) probiert, aber alles ohne Erfolg.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Manchmal liegt es aber auch daran, dass nicht alle Strings im Code als Unicode abgelegt sind ... ;-)
Das Leben ist wie ein Tennisball.
peddy
User
Beiträge: 121
Registriert: Montag 30. Juni 2008, 13:51

EyDu hat geschrieben:Manchmal liegt es aber auch daran, dass nicht alle Strings im Code als Unicode abgelegt sind ... ;-)
Mhhh, die Felder für Name und Vorname sind doch alle Unicode ( u'Wert' ).
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Das sind aber nicht die einzigen strings ;D
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Ich kann dein Problem nicht so ganz nachvollziehen, auch unter Python2 hat man doch eigentlich keine Probleme mit Zeichen außerhalb des ASCII Bereichs zuarbeiten.

Du schreibst in deine Datei ein:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
am Anfang, schreibst alles so wie du es normal auch tust, also ohne manuell zu escapen und ohne unicode literals und fertig.

Wenn Daten von außerhalb kommen z.B aus einer Datenbank dann stellt man dort als encodeing noch UTF-8 ein und fertig. Oder wenn das nicht geht, ich glaube bei ConfigParser ist das so?, hantiert man bevor man mit den Daten arbeitet mit einem value.encode("utf8"), bzw. .decode("utf8") je nachdem ob man speicher oder ob man liest.

*edit* noch etwas, dein "\xf6" ist latin-1 kein utf-8 ("\xc3\xb6")
peddy
User
Beiträge: 121
Registriert: Montag 30. Juni 2008, 13:51

Sr4l hat geschrieben:Ich kann dein Problem nicht so ganz nachvollziehen, auch unter Python2 hat man doch eigentlich keine Probleme mit Zeichen außerhalb des ASCII Bereichs zuarbeiten.

Du schreibst in deine Datei ein:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
am Anfang, schreibst alles so wie du es normal auch tust, also ohne manuell zu escapen und ohne unicode literals und fertig.
Das geht nicht in meinem Fall. Das Script ist nur von mir vereinfacht worden, damit man den Fehler leichter nachvollziehen kann. Den Unicode bekomme ich ursprünglich von einer SQL-Datenbank.
Sr4l hat geschrieben: Wenn Daten von außerhalb kommen z.B aus einer Datenbank dann stellt man dort als encodeing noch UTF-8 ein und fertig. Oder wenn das nicht geht, ich glaube bei ConfigParser ist das so?, hantiert man bevor man mit den Daten arbeitet mit einem value.encode("utf8"), bzw. .decode("utf8") je nachdem ob man speicher oder ob man liest.
Weiter oben wurde mir empfohlen meinen Text als Unicode zusammen zu bauen und erst am Schluss ein encode zu machen. Daher habe ich mein Augenmerk erst mal auf dieses Verfahren gerichtet.
BlackJack

@peddy: Du suchst das Problem wahrscheinlich an falscher Stelle. Trenne mal das erstellen des auszugebenen Textes von der Ausgabe mit ``print``. Dann wirst Du sehr wahrscheinlich feststellen, dass die Ausgabe das Problem ist. Du kannst Unicode nicht (immer) einfach so ausgeben, denn wenn die Zeichen das Programm verlassen müssen sie an irgendeiner Stelle wieder in Bytes umgewandelt werden. Und wenn Python an der Stelle nicht raten kann mit welcher Kodierung das passieren soll, wird ASCII genommen. Wenn dann nicht alles im ASCII-Bereich ist, dann gibt es diesen Fehler.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

peddy hat geschrieben:Das geht nicht in meinem Fall. Das Script ist nur von mir vereinfacht worden, damit man den Fehler leichter nachvollziehen kann. Den Unicode bekomme ich ursprünglich von einer SQL-Datenbank.
Das habe ich gelesen, das Verhalten von Datenbanken kann man aber ändern.
peddy hat geschrieben:Weiter oben wurde mir empfohlen meinen Text als Unicode zusammen zu bauen und erst am Schluss ein encode zu machen. Daher habe ich mein Augenmerk erst mal auf dieses Verfahren gerichtet.
Wenn du weiterhin überall mit Unicode arbeiten willst dann musst du, das mit dienen literals auch durchziehen, also auch in den Strings vom formatieren '{0} {1}'.

Oder du versuchts es mit einem, future import, da bin ich mir jedoch nicht sicher was das alles für Seiteneffekte haben kann, am Anfang.

Code: Alles auswählen

from __future__ import unicode_literals
BlackJack

@Sr4l: Das muss man nicht mit den Literalen Zeichenketten durchziehen. Nur wenn da etwas ausserhalb von ASCII enthalten ist. Dann fliegt einem an der entsprechenden Stelle aber auch eine Ausnahme um die Ohren.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Sr4l hat geschrieben:*edit* noch etwas, dein "\xf6" ist latin-1 kein utf-8 ("\xc3\xb6")
Die escape-Sequenz \x.. in Unicode-Strings bedeutet einfach, dass ein Unicode-Zeichen als 2-stellige Hexzahl angegeben wird
und ist äquivalent zu \u00xx.
Solange sich Strings auf 7bit beschränken lassen sich Unicode- und Byte-Strings in Python ohne weitere Umwandlung kombinieren.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

BlackJack hat geschrieben:@Sr4l: Das muss man nicht mit den Literalen Zeichenketten durchziehen. Nur wenn da etwas ausserhalb von ASCII enthalten ist. Dann fliegt einem an der entsprechenden Stelle aber auch eine Ausnahme um die Ohren.
Kann dir jetzt nicht ganz folgen. Mit "nur wenn außerhalb von ASCII" meinst du: "kein literal", u"beim bösen aber schon" ?

Bei mir funktioniert das aber nicht mit .format().

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
for name in [u"a", u"ä", u"ö", u"ü"]:
    print repr(name)
    print repr(u"{0}".format(name))
    print repr("{0}".format(name))
u'a'
u'a'
'a'
u'\xe4'
u'\xe4'
Traceback (most recent call last):
File "test.py", line 6, in <module>
print repr("{0}".format(name))
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 0: ordinal not in range(128)
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Code: Alles auswählen

In [1]: u = u"ä"

In [2]: s = "s"

In [3]: s + u
Out[3]: u's\xe4'

In [4]: "%s%s"%(s, u)
Out[4]: u's\xe4'

In [5]: "{} {}".format(s, u)
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-5-800d004aa231> in <module>()
----> 1 "{} {}".format(s, u)

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

In [6]: " ".join([s, u])
Out[6]: u's \xe4'

In [7]: 
Das ist ein Bug oder?

Alle Varianten von str + unicode ergeben unicode, außer bei format?!
BlackJack

@Sr4l: `str.format()` scheint tatsächlich nur mit `str` beziehungsweise `unicode` im ASCII-Bereich klar zu kommen. Bin ich bis jetzt noch nicht drüber gestolpert — ich verwende wohl immer noch zu oft den ``%``-Operator. :-)
Antworten