Umlaute und len()...

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
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Guten morgen...

... es ist zum verrückt werden! Ich hatte das Problem schon einmal, finde die damalige Lösung aber weder in meinem Hirn noch hier über die Suche...

Code: Alles auswählen

In [35]: month = 'März'

In [36]: month
Out[36]: 'M\xc3\xa4rz'

In [37]: len(month)
Out[37]: 5
:? Ich verstehe gerade noch soviel, dass der Umlaut 'ä' aus irgendeinem Grund 2 'Stellen' belegt.

Wie bekomme ich den 4-stelligen 'März' in 'month' hinein?

Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Hilft das?

Code: Alles auswählen

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> month = u'März'
>>> len(month)
4
>>>
BlackJack

@mutetella: Das ist ein UTF-8 kodiertes 'ä'. Wenn Du mit Text arbeiten möchtest, solltest Du `unicode` verwenden. Allerdings ist auch dort nicht sichergestellt, dass ein Buchstabe nur einen Index in der Zeichenkette umfasst.

Code: Alles auswählen

In [181]: m = 'März'.decode('utf-8')

In [182]: m
Out[182]: u'M\xe4rz'

In [183]: len(m)
Out[183]: 4

In [184]: unicodedata.normalize('NFD', m)
Out[184]: u'Ma\u0308rz'

In [185]: len(unicodedata.normalize('NFD', m))
Out[185]: 5
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wow... das artet ja in Arbeit aus... :wink:

Ich gehe also wie folgt vor:
  1. Ich lese aus einer UTF-8 codierten config-Datei diverse Strings (darunter eben auch der 'März') ein
  2. Diese Strings wandle ich zur internen Verarbeitung mit str.decode('utf8') in unicode um
  3. Jedes mal, bevor ich einen dieser unicode-Strings auf dem Bildschirm ausgebe (oder mit str.format() verwende), codiere ich ihn mit str.encode('utf8') wieder um.
Ist das so richtig?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Zu 3.: Wieso bei `str.format()`?
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack:
Das habe ich mich auch gefragt.

Code: Alles auswählen

In [143]: '{0}'.format('März')
Out[143]: 'M\xc3\xa4rz'

In [144]: '{0}'.format(u'März')
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)

/home/claus/Daten/Python/iPython/<ipython console> in <module>()

UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-2: ordinal not in range(128)
:K

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

mutetella hat geschrieben:@BlackJack:
Das habe ich mich auch gefragt.

Code: Alles auswählen

In [143]: '{0}'.format('März')
Out[143]: 'M\xc3\xa4rz'

In [144]: '{0}'.format(u'März')
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)

/home/claus/Daten/Python/iPython/<ipython console> in <module>()

UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-2: ordinal not in range(128)
:K

mutetella
Ist doch ganz klar: Bei 143 sind beides Bytestrings, die in der Kodierung der Python-Shell vorliegen. Diese kann offensichtlich mit Umlauten umgehen - die __repr__-Darstellung kommt da ja nur mangels Ausgabe.

Bei 144 ist die Sache anders: Du willst in einen Bytestring einen Unicodestring einfügen. Das impliziert eine Konvertierung. Python wandelt also den Unicodestring in einen Bytestring um. Das Default-encoding ist da eben ASCII; das umfasst aber ja nun mal keine Umlaute und daher krachts.

Probier das Bsp. aus 144 mal ohne das "ä" und lass Dir den typen des Ergebnisses anzeigen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mutetella hat geschrieben:

Code: Alles auswählen

In [144]: '{0}'.format(u'März')
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)

/home/claus/Daten/Python/iPython/<ipython console> in <module>()

UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-2: ordinal not in range(128)
Schnarchsack!

Code: Alles auswählen

'{0}'.format(u'März')
Ich verwende in meinem aktuellen Python 2 Code ohnehin immer

Code: Alles auswählen

from __future__ import unicode_literals
BlackJack

@mutetella:

Code: Alles auswählen

In [201]: u'{0}'.format('März'.decode('utf-8'))
Out[201]: u'M\xe4rz'
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Code: Alles auswählen

In [86]: template = '{0} {1}'.decode('utf-8')

In [87]: m = 'März'.decode('utf-8')

In [88]: y = '2011'.decode('utf-8')

In [89]: len(template.format(m, y))
Out[89]: 9

In [90]: template = u'{0} {1}'

In [91]: m = u'März'

In [92]: y = u'2011'

In [93]: len(template.format(m, y))
Out[93]: 10
Warum?
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Warum?
Weil Unicode-literale in Ipython nicht richtig funktionieren:

Code: Alles auswählen

# Windows XP:
# Falsch
In [1]: u"März"
Out[1]: u'M\x84rz'
# richtig:
In [2]: "März".decode('cp850')
Out[2]: u'M\xe4rz'#
Man kann sowas wie "u=lambda s: s.decode(...)" als Würg-around benutzen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

mutetella hat geschrieben: Warum?
Warum nach > 300 Postings keine Python-Code-Tags? :twisted:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mutetella hat geschrieben:

Code: Alles auswählen

In [87]: m = 'März'.decode('utf-8')
Was soll das denn für ein Konstrukt sein und warum geht das? 'März' ist doch gar keine gültige UTF-8-Codierung. Verstanden hätte ich ja

Code: Alles auswählen

m = 'März'.decode('iso-8859-15')
mutetella hat geschrieben:

Code: Alles auswählen

In [90]: template = u'{0} {1}'

In [91]: m = u'März'

In [92]: y = u'2011'

In [93]: len(template.format(m, y))
Out[93]: 10
In IDLE sieht es bei mir wie folgt aus:

Code: Alles auswählen

>>> template = u'{0} {1}'
>>> m = u'März'
>>> y = u'2011'
>>> len(template.format(m, y))
9
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

/me hat geschrieben:
mutetella hat geschrieben:

Code: Alles auswählen

In [87]: m = 'März'.decode('utf-8')
Was soll das denn für ein Konstrukt sein und warum geht das? 'März' ist doch gar keine gültige UTF-8-Codierung.
Das ist ein workaround weil das Terminal hier noch Nebeneffekte verursacht.

Am besten probiert man sowas erst gar nicht in einer Shell aus.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

besser.wisser hat geschrieben:Weil Unicode-literale in Ipython nicht richtig funktionieren:
Ok, in der Pythonshell stimmt's dann auch:

Code: Alles auswählen

>>> template = u'{0} {1}'
>>> m = u'März'
>>> y = u'2011'
>>> len(template.format(m, y))
9
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@/me: Woher willst Du wissen wie 'März' bei jemand anders kodiert ist? Wieso hättest Du ``m = 'März'.decode('iso-8859-15')`` verstanden? Das macht doch nur das Richtige wenn 'März' wirklich in iso-8859-15 kodiert ist.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat geschrieben:@/me: Woher willst Du wissen wie 'März' bei jemand anders kodiert ist? Wieso hättest Du ``m = 'März'.decode('iso-8859-15')`` verstanden? Das macht doch nur das Richtige wenn 'März' wirklich in iso-8859-15 kodiert ist.
Du hast natürlich recht. Ich hatte das mögliche Encodingformat der Datei (bzw. der Entwicklungsumgebung) außer Acht gelassen und einfach mit IDLE im Default-Modus getestet.
Antworten