Formattieren von Nicht-7-bit-Strings

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.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich dachte du haettest dir die Praesentation schon angeschaut? Du hast es aber scheinbar noch nicht ganz verdaut (es ist - zugegeben - nicht ganz einfach). Also nochmal durcharbeiten!

Python 2 arbeitet mit Byte-Strings, aus der Datenbank bekommst du mit Latin1 kodierte Byte-Strings. Python 2 dekodiert unbekannte Byte-Strings nunmal mit ASCII (in Python 3 mit utf-8). Und Peng!

Damit dein Vorhaben funktioniert, musst du deine Byte-Strings zu Unicode Objekten dekodieren (per `decode` mit dem entsprechenden Encoding.)
AFAIR gibt es zu mysqldb aber auch eine Encoding Option mit der man direkt `unicode` Objekte aus Text-Feldern bekommt.
Benutzeravatar
kajarno
User
Beiträge: 26
Registriert: Samstag 16. Januar 2010, 12:41
Wohnort: München
Kontaktdaten:

cofi hat geschrieben:Ich dachte du haettest dir die Praesentation schon angeschaut? Du hast es aber scheinbar noch nicht ganz verdaut (es ist - zugegeben - nicht ganz einfach). Also nochmal durcharbeiten!

Python 2 arbeitet mit Byte-Strings, aus der Datenbank bekommst du mit Latin1 kodierte Byte-Strings. Python 2 dekodiert unbekannte Byte-Strings nunmal mit ASCII (in Python 3 mit utf-8). Und Peng!

Damit dein Vorhaben funktioniert, musst du deine Byte-Strings zu Unicode Objekten dekodieren (per `decode` mit dem entsprechenden Encoding.)
AFAIR gibt es zu mysqldb aber auch eine Encoding Option mit der man direkt `unicode` Objekte aus Text-Feldern bekommt.
Cofi: wenn Du mit "die Praesentation" http://wiki.python-forum.de/Von%20Umlau ... 0Encodings meinst, ja, ich habe sie gelesen. Auch versucht umzusetzen. Aber die Längen kriege ich nie richtig hin, aus der Datenbank. Encoden und decoden tue ich hier so:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import codecs
import sys
import MySQLdb

db = MySQLdb.connect(host='localhost', user='testuser', passwd='testpw',db="test")
c = db.cursor()
c.execute("select * from umlaute")
selectresult = c.fetchall()
for item in selectresult:
  umlautfld = item[0]
  print umlautfld, len(umlautfld);

for item in selectresult:
  umlautfld = item[0]
  s_unicode = umlautfld.decode("iso-8859-1")
  s_utf8 = s_unicode.encode("utf-8")
  print "types:", type(umlautfld),type(s_unicode), type(s_utf8)
  print " len",umlautfld,len(umlautfld),len(s_unicode),len(s_utf8)
Das Ergebnis ist nicht erfreulich, denn "str" sagt wenig darüber aus, ob Python nun von Ascii, Latin1 oder UTF-8 ausgeht, und "Gemüse" hat entweder eine falsche Länge von 7, oder eine sehr falsche von 9:

Code: Alles auswählen

kaj@Birger[kajtajm]$ umlautbeispiel.py 
Gemüse 7
Obst 4
München 8
1¾ 3
Herrlich 8
Wunderbar 9
types: <type 'str'> <type 'unicode'> <type 'str'>
 len Gemüse 7 7 9
types: <type 'str'> <type 'unicode'> <type 'str'>
 len Obst 4 4 4
types: <type 'str'> <type 'unicode'> <type 'str'>
 len München 8 8 10
types: <type 'str'> <type 'unicode'> <type 'str'>
 len 1¾ 3 3 5
types: <type 'str'> <type 'unicode'> <type 'str'>
 len Herrlich 8 8 8
types: <type 'str'> <type 'unicode'> <type 'str'>
 len Wunderbar 9 9 9
Kaj Arnö, Sun VP MySQL Community Relations
Vormals (1979-99) Programmierer, möchte 2010 alte Künste wiederbeleben, und zwar mit PyObjC und MySQLdb unter Mac. Aus Finnland, Muttersprache Schwedisch. Bevorzugt Deutsch über Englisch. In München seit 2006.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Du kannst MySQLdb.connect noch ein paar Einstellungen mit geben wie charset="utf8" und use_unicode=True

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
kajarno
User
Beiträge: 26
Registriert: Samstag 16. Januar 2010, 12:41
Wohnort: München
Kontaktdaten:

jens hat geschrieben:Du kannst MySQLdb.connect noch ein paar Einstellungen mit geben wie charset="utf8" und use_unicode=True
Jens, ja, habe ich probiert (hätte ich sagen sollen). Half auch nicht. Die Daten konnten nicht von Python als Unicode mit richtiger "len("-Berechnung behandelt werden.
Kaj Arnö, Sun VP MySQL Community Relations
Vormals (1979-99) Programmierer, möchte 2010 alte Künste wiederbeleben, und zwar mit PyObjC und MySQLdb unter Mac. Aus Finnland, Muttersprache Schwedisch. Bevorzugt Deutsch über Englisch. In München seit 2006.
BlackJack

Vielleicht solltest Du auch mal ausgeben was die Zeichen- bzw. Byteketten *wirklich* enthalten, also mit `repr()` damit es keine Rolle spielt was die Anwendung die das darstellt, erwartet und interpretiert. Für mich sieht das so aus, als wenn Du aus der Datenbank `str`, also Byteketten, in UTF-8 Kodierung bekommst. Und dann stimmen die Längen die da stehen. "Gemüse" hat UTF-8-kodiert eine Länge von 7 Bytes. Wenn man diese als ISO-8851-1 dekodiert bekommt man 7 Unicode-Zeichen. Da sind zwei die nicht von ASCII abgedeckt werden, das werden dann also als UTF-8 kodiert 9 Bytes.

Code: Alles auswählen

In [48]: s
Out[48]: 'Gem\xc3\xbcse'

In [49]: len(s)
Out[49]: 7

In [50]: print s
Gemüse

In [51]: u = s.decode('iso-8859-1')

In [52]: u
Out[52]: u'Gem\xc3\xbcse'

In [53]: len(u)
Out[53]: 7

In [54]: print u
Gemüse

In [55]: s2 = u.encode('UTF-8')

In [56]: s2
Out[56]: 'Gem\xc3\x83\xc2\xbcse'

In [57]: len(s2)
Out[57]: 9

In [58]: print s2
Gemüse
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

kajarno hat geschrieben:Cofi: wenn Du mit "die Praesentation" http://wiki.python-forum.de/Von%20Umlau ... 0Encodings meinst, ja, ich habe sie gelesen.
Nein, Cofi meinte die Präsentation mit den richtigen Folien, als PDF.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Leonidas hat geschrieben:Nein, Cofi meinte die Präsentation mit den richtigen Folien, als PDF.
Die hier (Die hat acuh schon Hyperion gepostet - im 5. Post dieses Threads)
Benutzeravatar
kajarno
User
Beiträge: 26
Registriert: Samstag 16. Januar 2010, 12:41
Wohnort: München
Kontaktdaten:

cofi hat geschrieben:
Leonidas hat geschrieben:Nein, Cofi meinte die Präsentation mit den richtigen Folien, als PDF.
Die hier (Die hat acuh schon Hyperion gepostet - im 5. Post dieses Threads)
Danke! Jetzt gelesen. Ist komplizierter als erwünscht. "Man kann das Encoding nicht zuverlässig erraten", nicht gerade ermutigend.

Also habe ich die Lösung selbst gebastelt, denn eigentlich ist alles was ich brauche eine Funktion, die die *Druckbreite* eines Strings errechnet, anstatt die Speicherbreite (die beim Drucken herzlich egal ist).

Code: Alles auswählen

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

db = MySQLdb.connect(host='localhost', user='testuser', passwd='testpw',db="test")
c = db.cursor()
c.execute("select * from umlaute")
selectresult = c.fetchall()
for item in selectresult:
  umlautfld = item[0]
  iUmlaute = 0
  for zeichen in umlautfld:
    if ord(zeichen) < 48 or ord(zeichen) > 122:
      iUmlaute += 1
  iUmlaute = iUmlaute / 2
  druckbreite = len(umlautfld) - iUmlaute
  umlautfld += " " * (9-druckbreite)
  print umlautfld, druckbreite, "Nicht schief"
Voilà:

Code: Alles auswählen

kaj@Birger[kajtajm]$ umlautbeispiel.py 
Gemüse    6 Nicht schief
Obst      4 Nicht schief
München   7 Nicht schief
1¾        2 Nicht schief
Herrlich  8 Nicht schief
Wunderbar 9 Nicht schief
Ok, die ord()-Werte von 48 und 122 passen vielleicht nur mir und nur in diesem Zusammenhang. Wahrscheinlich sind sie nicht überall gültig. Aber das Problem ist vorerst gelöst. Danke an alle, die geholfen haben: Darii, cofi, fhoech, BlackJack, jens, EyDu, Hyperion, Leonidas! Ich bin beeindruckt und dankbar für die Hilfsbereitschaft und das Sachverständnis der Foren-Mitglieder.
Kaj Arnö, Sun VP MySQL Community Relations
Vormals (1979-99) Programmierer, möchte 2010 alte Künste wiederbeleben, und zwar mit PyObjC und MySQLdb unter Mac. Aus Finnland, Muttersprache Schwedisch. Bevorzugt Deutsch über Englisch. In München seit 2006.
BlackJack

@kajarno: Das ist ziemlicher Murks, Du solltest wirklich lieber versuchen zu *verstehen* wie das mit Unicode funktioniert. Denn in aller Regel ist die Länge einer Unicode-Zeichenkette, zumindest in unseren Breitengraden (und wenn es nicht gerade um MacOS' Dateinamen geht), auch die Anzahl der Zeichen, die ausgegeben werden.

In Deinem Fall muss man die Kodierung nicht erraten, denn Du weisst ja in welcher Kodierung Du die Daten in die Datenbank steckst und in welcher Du sie heraus bekommst. Also *Du* weisst das jetzt nicht, aber das ist etwas das man wissen *sollte*. Wenn ich mir die `mysql`-Sitzung weiter oben anschaue, die ja wohl in einem Terminal eingegeben wurde, das UTF-8 "spricht", dann würde ich mal darauf tippen, dass die Daten als UTF-8 in der DB stehen. Du gehst aber aus irgendeinem Grund davon aus, dass es Latin1 ist!? Warum? Wenn die DB auch davon ausgeht, dann liefert sie natürlich auch falsche Daten wenn Du sagst Du möchtest das Latin1, was keines ist, als Unicode-Objekte haben.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

kajarno hat geschrieben:Also habe ich die Lösung selbst gebastelt, denn eigentlich ist alles was ich brauche eine Funktion, die die *Druckbreite* eines Strings errechnet, anstatt die Speicherbreite (die beim Drucken herzlich egal ist).
Wie BlackJack schon schrieb, lass das lieber und versuch zu verstehen, wie das mit den Zeichenkodierungen funktioniert, sonst wirst du bestimmt bald wieder irgendwelche Probleme kriegen.

Die Druckbreite zu bestimmen ist übrigens wirklich nicht so einfach.

Code: Alles auswählen

>>> ae = unicodedata.normalize("NFD", u"ä")
>>> print ae, len(ae), repr(ae)
ä 2 u'a\u0308'
Ein druckbares Zeichen kann nämlich durchaus aus mehreren Zeichen zusammengesetzt sein.
Benutzeravatar
kajarno
User
Beiträge: 26
Registriert: Samstag 16. Januar 2010, 12:41
Wohnort: München
Kontaktdaten:

Darii, BlackJack: Ich glaube nicht dass ich das Thema Unicode jetzt für immer los geworden bin! Die momentane Frustration darüber, dass eine einfache Sache wie die Berechnung der Breite einer Zeichenkette mein Fortschritt verhindert, habe ich allerdings überwunden.

Die Datenbank ist in latin1 weil das auch so von MySQL gemeldet wird (steht oben in einem code-Abschnitt). Eines Tages könnte ich versuchen, die Daten mit UTF-8 in MySQL anzulegen. Dann befürchte ich, später mal Probleme bei der Dateneingabe direkt unter MySQL, bzw. mit der Kommunikation mit Flatfiles zu bekommen, das alles bereiche sind, wo es heute problemlos läuft.

Zum Experimentieren um die richtige Lösung zu finden bin ich hoffentlich bei dem nächsten Unicode-Problem bereit.

Breite von "ä": Huch, wenn len("ä") = 2 auch wenn man alles "richtig" eingibt, ist ja komisch. In den Breitengraden aus denen ich komme (Finnland, Schweden usw.) ist "ä" ein (1) Buchstabe und keineswegs ein "zusammengesetztes Zeichen" oder irgendeine Spezialform von "a" (wie es hier südlich vom Ostsee der Fall zu sein scheint). "ä" kommt sortiermäßig auch nach "z" (... xyzåäö). Also würde in diesem Falle die "richtige" Lösung überhaupt nicht die tatsächlich richtige Lösung sein, zumindest aus meiner Sicht (locale).
Darii hat geschrieben:
kajarno hat geschrieben:Also habe ich die Lösung selbst gebastelt, denn eigentlich ist alles was ich brauche eine Funktion, die die *Druckbreite* eines Strings errechnet, anstatt die Speicherbreite (die beim Drucken herzlich egal ist).
Wie BlackJack schon schrieb, lass das lieber und versuch zu verstehen, wie das mit den Zeichenkodierungen funktioniert, sonst wirst du bestimmt bald wieder irgendwelche Probleme kriegen.

Die Druckbreite zu bestimmen ist übrigens wirklich nicht so einfach.

Code: Alles auswählen

>>> ae = unicodedata.normalize("NFD", u"ä")
>>> print ae, len(ae), repr(ae)
ä 2 u'a\u0308'
Ein druckbares Zeichen kann nämlich durchaus aus mehreren Zeichen zusammengesetzt sein.
Kaj Arnö, Sun VP MySQL Community Relations
Vormals (1979-99) Programmierer, möchte 2010 alte Künste wiederbeleben, und zwar mit PyObjC und MySQLdb unter Mac. Aus Finnland, Muttersprache Schwedisch. Bevorzugt Deutsch über Englisch. In München seit 2006.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

kajarno hat geschrieben:In den Breitengraden aus denen ich komme (Finnland, Schweden usw.) ist "ä" ein (1) Buchstabe und keineswegs ein "zusammengesetztes Zeichen" oder irgendeine Spezialform von "a" (wie es hier südlich vom Ostsee der Fall zu sein scheint).
Das hat nicht sonderlich viel mit locales zu tun, sondern mit dem Unicode-Standard, der eben verschiedene Normalformen definiert. Je nachdem ist ä eben ein Kompisitum aus a und Diaeresis(Combining Diaeresis) oder ein einzelner Buchstabe(a with Diaeresis).
Aber das kann die Doku viel besser als ich: http://docs.python.org/library/unicoded ... .normalize
BlackJack

@kajarno: Was die Datenbank annimmt welche Kodierung sie bekommt, und welche sie tatsächlich bekommt, sind zwei verschiedene Dinge. Und ich denke Du hast da falsche/kaputte Daten in der DB. Du sagtest ja selbst, dass MySQL die Länge auch "falsch" berechnet. Was höchstwahrscheinlich daran liegt, das in der DB die "denkt" Latin1 zu enthalten, tatsächlich UTF-8 drin ist.

Wenn eine Datenbank/Tabelle auf Latin1 eingestellt ist, müssen die Daten auch so kodiert da reingeschrieben werden. Man kann allerdings auch problemlos UTF-8 kodierte Umlaute in so eine Tabelle schreiben, weil das ja in Latin1 keine illegallen Bytefolgen sind. Sie bedeuten halt nur etwas anderes, aber das weis die Datenbanksoftware ja nicht.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

kajarno hat geschrieben:Breite von "ä": Huch, wenn len("ä") = 2 auch wenn man alles "richtig" eingibt, ist ja komisch. In den Breitengraden aus denen ich komme (Finnland, Schweden usw.) ist "ä" ein (1) Buchstabe und keineswegs ein "zusammengesetztes Zeichen" oder irgendeine Spezialform von "a" (wie es hier südlich vom Ostsee der Fall zu sein scheint).
Das mit der Normalform war auch nur ein Beispiel(wenn auch ein wichtiges, denn OSX kodiert seine Dateinamen so und kommt leider mit anders kodierten nicht zurecht). Er gibt im Grunde genommen nur eine sichere Methode herauszufinden wie „breit“ ein String ist: Rendern lassen und gucken was dabei rauskommt. Alles andere führt zwangläufig zu Fehlern.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Mach doch mal sowas:

Code: Alles auswählen

MYSQL_ENCODING_VARS = ("character_set_server", "character_set_connection", "character_set_results", "collation_connection",)
for var_name in MYSQL_ENCODING_VARS:
    cursor.execute("SHOW VARIABLES LIKE %s;", (var_name,))
    print var_name, cursor.fetchall()
Ich hab auch einen einfachen Test geschrieben bei dem ich mit unichr() einen Test string in unicode erzeuge. Der wird dann in die Datenbank eingefügt und gleich wieder herraus geholt und mit dem Original verglichen.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten