Encoding-Problem

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Hallo,

ich hab jetzt schon ne ganze Weile hier gesucht und auch jede Menge zu dem Thema gefunden udn viel rumprobiert, aber irgendwie komme ich nicht weiter.

Ich lese aus einem *.csv-File Adressen aus und will die in eine Oracle Datenbank schreiben.
Dort Sachen rein zu schreiben, hatte ich bisher noch nie Probleme mit Umlauten.

Allerdings habe ich jetzt halt diese CSV von einem Kunden und ich krieg die eingelesenen Zeilen nicht mal in der Console von MyEclipse oder in der Windowskonsole richtig ausgegeben, hab ständig Umlaut-Fehler, so wird z.B ein ä immer als \xe4 ausgegeben, in der Windowskonsole als o mit so nem komischen Zeichen drüber.

Hab dann probiert:

Code: Alles auswählen

coding = "iso-8859-1"
insert_list.append(row[fields["Strasse"]].encode(coding)+ " " + str(i))
# row[fields["Strasse"]] liefert mir als String den Straßennamen
Aber macht keinen Unterschied, das gleich Problem. Wenn ich dass dann aber in der WIndows Konsole und nicht im MyEclipse Starte kommt folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "x:\xxx\ccc\eee\src\create_adresses.py", line 108, in
<module>
    main(infile)
  File "x:\xxx\ccc\eee\src\create_adresses.py", line 99, in main
    readcsv(infile,db)
  File "x:\xxx\ccc\eee\src\create_adresses.py", line 71, in readcsv
    values = build_insert_list(row,i)
  File "x:\xxx\ccc\eee\src\create_adresses.py", line 38, in build_insert_list
    insert_list.append(row[fields["Kirchenbezirk"]].encode(coding))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfc in position 1: ordinal not in range(128)
Was ist das für ein Fehler? Und warum wird der in MyEclipse NICHT geworfen?
Und wie bekomme ich nun meine Adressen richtig codiert. Ich nehme an, wenn ich in der Console richtige Umlaute sehe, werden sie dann auch so in die Datenbank geschrieben.

Ein Kollege hat auch irgendwo ein kleines Modul gefunden:

Code: Alles auswählen

import csv, codecs, cStringIO

class UTF8Recoder:
    """
    Iterator that reads an encoded stream and reencodes the input to UTF-8
    """
    def __init__(self, f, encoding):
        self.reader = codecs.getreader(encoding)(f)

    def __iter__(self):
        return self

    def next(self):
        return self.reader.next().encode("utf-8")

class UnicodeReader:
    """
    A CSV reader which will iterate over lines in the CSV file "f",
    which is encoded in the given encoding.
    """

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        f = UTF8Recoder(f, encoding)
        self.reader = csv.reader(f, dialect=dialect, **kwds)

    def next(self):
        row = self.reader.next()
        return [unicode(s, "utf-8") for s in row]

    def __iter__(self):
        return self

class UnicodeWriter:
    """
    A CSV writer which will write rows to CSV file "f",
    which is encoded in the given encoding.
    """

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        self.writer.writerow([s.encode("utf-8") for s in row])
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for row in rows:
            self.writerow(row)
Das haben wir dann benutzt und das ganze folgender Maßen eingebunden:

Code: Alles auswählen

csv_file = coding.UnicodeReader(open(infile), delimiter=';', encoding="iso-8859-15")
Aber da bekam ich Fehler von der Datenbank, dass das keine Strings seien.

Wäre für Hinweise sehr Dankbar, denn komme im Moment nicht so richtig weiter.

Grüße.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Vorweg zwei Fragen: welches Encoding will die Datenbank haben? Welches Encoding ist in der CSV-Datei benutzt?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

welches die CSV Datei hat, weiß ich nicht und leider auch nicht, wie ich das rausbekomme. Ich habe die so vom Kunden bekommen.
Die Datenbank:WE8MSWIN1252 was auch immer das ist. Zumindest war das die Antwort unserers DBAs auf meine Anfrage dazu gestern.

Aber schon bevor die Daten in die Datenbank kommen, hatte ich mir nun zu Testzwecken einfach die "INSERT"-Statements ausgeben lassen und dort treten halt schon die Encoding-Fehler auf.
Allerdings erst dort. Ich übergebe die Werte ja aus einem Tupel, die in die DB eingefügt werden sollen und wenn ich mir die Liste, aus dem ich das Tupel mache ausgeben lassen, sind alle Umlaute korrekt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

meneliel hat geschrieben:welches die CSV Datei hat, weiß ich nicht und leider auch nicht, wie ich das rausbekomme.
Du öffnest sie in einem Editor als Latin-9 (ISO-8859-15) und guckst ob "Sonderzeichen" passen, wenn nicht dann als UTF-8 usw. Bis dann irgendwann alles passt.
meneliel hat geschrieben:Die Datenbank:WE8MSWIN1252 was auch immer das ist.
Also musst du in deinem Python-Programm höchstwarscheinlich CP-1252 als Ausgabeencoding nutzen.
meneliel hat geschrieben:Aber schon bevor die Daten in die Datenbank kommen, hatte ich mir nun zu Testzwecken einfach die "INSERT"-Statements ausgeben lassen und dort treten halt schon die Encoding-Fehler auf.
Das muss nicht sein: \xe4 ist kein Escape-Fehler sondern ein Zeichen, dessen ASCII-Wert über 128 ist, und wird daher vom Python-Interpreter escaped. In der Windowskonsole können auch richtige Zeichen falsch dargestellt werden, wenn das Encoding der AUsgabe nicht mit dem Encoding welches die Konsole erwartet, übereinstimmt.

Die Verarbeitung sieht so aus:
Eingabe -> CSV -> in Unicode mit Codec X wandeln -> Verarbeitung -> In CP-1252 enkodierte Bytestrings konvertieren -> an die Datenbank weitergeben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Danke für die Hniweise.

Die Sache ist: im Normalen Text-Editor öffne, seh ich alle Umlaute richtig. geh ich auf Speichern unter, ist vorausgewählt ANSI, müsste dann ja iso-8859-15 sein, oder? Bei der Datei sieht auch alles richtig aus, wenn ich sie im Ultra-Edit öffne.

Ich weiß nicht, und hab nix gefunden, wie ich eine Dtei gezielt mit einem Coding öffnen kann :(

Hab dann zum Testen folgendes probiert: hab die Datei einfach mal im Editor als UTF-8 gespeichert und dann in der Zeile:

Code: Alles auswählen

csv_file = coding.UnicodeReader(open(infile), delimiter=';', encoding="utf-8")
angegeben.

Das cp-1252 müsste dann hier angegeben werden?

Code: Alles auswählen

coding = "CP-1252"
insert_list.append(row[fields["Strasse"]].encode(coding)+ " " + str(i))
Nur bekomm ich da zurück, dass dies ein unbekanntes Coding ist: LookupError: unknown encoding: cp-1252
Sowohl wenn ich cp klein, als auch wenn ich es groß schreibe.

Hab dann probiert, das ganze als utf-8 in die Datenbank zu schreiben: da bekomm ich noch mehr "komische" Sonderzeichen. Wenn ich zum Schluss probiere die Unicode-utf-8 Strings in iso-8859-15 in die DB zu schreiben bekomme ich die Meldung:
UnicodeEncodeError: 'charmap' codec can't encode character u'\ufeff' in position 0: character maps to <undefined>

Ist die Wahl des oben geposteten UnicodeReaders und der entsprechende Aufruf überhaupt der richtige Weg?[/code]
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

meneliel hat geschrieben:Die Sache ist: im Normalen Text-Editor öffne, seh ich alle Umlaute richtig. geh ich auf Speichern unter, ist vorausgewählt ANSI, müsste dann ja iso-8859-15 sein, oder?
Nein, denn sowas wie ANSI gibt es an sich gar nicht. Das wird wohl irgendein ISO 8859-Codec sein, aber welcher ist unbekannt.
meneliel hat geschrieben:Bei der Datei sieht auch alles richtig aus, wenn ich sie im Ultra-Edit öffne.
Mit welchem Encoding?
meneliel hat geschrieben:Ich weiß nicht, und hab nix gefunden, wie ich eine Dtei gezielt mit einem Coding öffnen kann :(
In Python oder im Editor? Wenn letzteres, dann solltest du dir einen besseren Editor besorgen, in sinnvollen Programmen kann man das Encoding zur Laufzeit ändern und gucken was passiert.
meneliel hat geschrieben:Hab dann probiert, das ganze als utf-8 in die Datenbank zu schreiben: da bekomm ich noch mehr "komische" Sonderzeichen.
Natürlich, was du machst ist ja auch Programming by Accident. Wenn du das verstehen willst, lies dir die Unicode-Seiten im Wiki durch und meine Folien vom 14. Usertreffen. Wenn es dir egal ist, dann befolge die Reihenfolge, die ich dir gepostet habe.
meneliel hat geschrieben:Ist die Wahl des oben geposteten UnicodeReaders und der entsprechende Aufruf überhaupt der richtige Weg?
Also ich würde ihn weglassen, das ist noch ein Stück Code dass zu unflexibel ist un nochmal extra angepasst werden müsste.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

meneliel hat geschrieben: Nur bekomm ich da zurück, dass dies ein unbekanntes Coding ist: LookupError: unknown encoding: cp-1252
Sowohl wenn ich cp klein, als auch wenn ich es groß schreibe.
Probiere mal "cp1252" ohne das "-" ;-)
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Hyperion hat geschrieben:
meneliel hat geschrieben: Nur bekomm ich da zurück, dass dies ein unbekanntes Coding ist: LookupError: unknown encoding: cp-1252
Sowohl wenn ich cp klein, als auch wenn ich es groß schreibe.
Probiere mal "cp1252" ohne das "-" ;-)
^^ ahhh ;-)

Führt aber bei oben genannten Vorgehen auch nicht zur Lösung und bringt keine Änderung. Dann such ich erst mal nach ner Alternative zu dem Unicode-Reader, um die aus der csv-ausgelesenen Zeile in Unicode umzuwandeln.

Im Ultra-Edit sieht es mit ISO 8859-xx richtig aus.
In Python oder im Editor?
hmm ... Beides? Meinte eigentlich im Editor. Dachte in Python geb ich das am Beginn des Moduls ein mit:
# -*- coding: iso-8859-1-*-

[/quote]
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

meneliel hat geschrieben:hmm ... Beides? Meinte eigentlich im Editor. Dachte in Python geb ich das am Beginn des Moduls ein mit:
# -*- coding: iso-8859-1-*-
Das bezeichnet nur das Encoding welches verwendet wird um u""-Strings in Unicode zu dekodieren.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

*langsam am Verzweifeln bin*

Also was ich aus der csv ausgelesen hab decode ich in unicode und mache hinterher wieder Byte-Strings daraus.

Hab mir jetzt die Seiten im Wiki durchgeguckt und dann auch noch die Folien vom Usertreffen und denke ich hab das soweit auch verstanden und scheint ja nun auch alles ganz logisch.

Dann müsste aber DAS funktionieren:

Code: Alles auswählen

coding_csv = "iso-8859-1"
# hab hier alternativ auch -15 probiert
coding_db = "cp1252"
insert_list.append(row[fields["PLZ"]].decode(coding_csv).encode(coding_db))
Keine Veränderung in der Datenbank - dort sehe ich statt einem ö z.B. immer noch ein umgedrehtes Fragezeichen.

:(
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Code: Alles auswählen

row[fields["PLZ"]]
Sicher, dass das auch ein Bytestring ist? Und vor allem, wieso wandeslt Du den nicht schon vorher in Unicode?

Hast Du mal einen Test durchgeführt und einen cp1252 kodierten Bytestring in die DB geschrieben? Evtl. stimmt das Encoding ja gar nicht?

Was genau bedeutet denn "dort sehe ich ..."? Was ist dort? Liest Du es wieder aus? Nutzt Du einen externen Viewer? (Wenn ja, kann der die Zeichen überhaupt richtig darstellen?)
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Hyperion hat geschrieben:

Code: Alles auswählen

row[fields["PLZ"]]
Sicher, dass das auch ein Bytestring ist? Und vor allem, wieso wandeslt Du den nicht schon vorher in Unicode?

Hast Du mal einen Test durchgeführt und einen cp1252 kodierten Bytestring in die DB geschrieben? Evtl. stimmt das Encoding ja gar nicht?

Was genau bedeutet denn "dort sehe ich ..."? Was ist dort? Liest Du es wieder aus? Nutzt Du einen externen Viewer? (Wenn ja, kann der die Zeichen überhaupt richtig darstellen?)
row ist eine Zeile in der csv, die hab ich zu dem zeitpunkt einfach nur geöffnet und gehe dann halt zeile für zeiel durch

fields["PLZ"] : fields ist ein Dictionary in dem ich abgespeichert habe, an welcher Stelle der CSV der entsprechende Wert steht, da ich ca. 20 Felder in der csv habe und nur 15 brauche, fand ich es einfach zu lesen den Index so zuzuordnen, als Zahl, ist im prinzip also nix anderes wie:

Code: Alles auswählen

csv_file = csv.reader(open(infile), delimiter=';')
for row in csv_file:
    # einiges an code, was mir i ermittelt
    i = xyz
    values = build_insert_list(row,i)

def build_insert_list(row,i):
    coding_csv = "iso-8859-1"
    coding_db = "cp1252"
    insert_list = list()
    insert_list.append(row[fields["PLZ"]].decode(coding_csv).encode(coding_db))
    ....
Wann kann/sollte ich die Werte aus row denn vorher umwandeln? Hatte es erst mit dem UniCode Reader, aber (vielleicht hab ich das auch net richtig verstanden) Leonidas meinte, den nicht verwenden.

Und was soll es sonst sein, wenn kein Bytestring? Unicode ist es jedenfalls nicht.
Und

Code: Alles auswählen

print type(row[fields["PLZ"]])
# gibt zurück:
<type 'str'>


Zumindest ist die CSV explizit als Windows-Ansi gespeichert wurden.

Ich erzuege doch cp1252 kodierte Bytestrings, die ich in die DB schreibe? Wie soll ich das anders testen?

Hab statt cp1252 auch schon iso-8859-1 und 15 und utf-8 und utf-16 probiert.

".. dort sehen..." heißt SQL_Developer und/oder ArcGIS ArcCatalog. Hab auch schon überlegt, dass es einfach nur falsch angezeigt wird und daher den nächste Verarbeitungsschritt probiert. Dort wurden dann aber die Adressen mit Umlauten nicht erkannt und auch dort mit Sonderzeichen ausgegeben, was am Ende heißt, das 1/3 der Adressen nicht gefunden werden kann, bei einem Testausschnitt der Daten.[/code]
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Naja, Du könntest ja einfach mal einen Unicode-String nehmen und explizit als cp1252 kodieren und dann in die DB schreiben.

Code: Alles auswählen

test = u"Hällö Wült!"
# Schreiben in die DB per test.encode("cp1252")
So weißt Du ja, dass in test auch wirklich das drinsteht, was Du willst / denkst. Wenn das klappt, so liegt das Problem def. noch bei Verarbeiten der csv-Daten. Wenn nicht, ist eben beim DB-Zugriff noch was im argen.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

*kopf gegen Wand schlag*

Ja klar... bin im Moment bisschen langsam und hatte nicth sofort gecheckt, was du meintest.

Also ein Test-Datensatz zu codieren und in die DB zu schreiben, einfach mit:

Code: Alles auswählen

test = u"üljkj äöiß"
x = test.encode("cp1252")
insert_string = "INSERT INTO ADRESSEN (Orts) VALUES('%s')" %x

UnicodeEncodeError: 'charmap' codec can't encode character u'\x9f' in position 13: character maps to <undefined>
Wirft schon beim encoding ein Problem mit dem ß, dass kann irgendwie nicht encodet werden. Ohne ß wird in die Datenbank geschrieben, allerdings mit gleichem Ergebnis wir letzte Woche: nur umgedrehte Fragezeichen sind zu sehen, keine Umlaute.

Wenn ich hingegen den Test-String oder eine ganze Zeile der Daten, die eigentlich in die DB sollen in eine TXT-Datei schreibe, sieht beim öffnen im Editor und Ultra-Edit alles richtig aus. Seltsamer weise wird beim Encoden hier kein Fehler geworfen, wenn ein ß vorkommt, mag aber daran liegen, dass ein eingelesener String ja erst in unicode decodiert wird und dann wieer encodiert?
BlackJack

@meneliel: Bei Deinem Test fehlt schon wieder etwas wichtiges: Wie ist die Quelltextdatei kodiert und hast Du das auch als "coding cookie"-Kommentar angegeben?

`test` enthält hier nämlich mit Sicherheit schon "Müll" weil ein echtes 'ß' problemlos als cp1252 kodiert werden kann.

Das die Fehlermeldung von "position 13" spricht, bei einem Unicode-Objekt, das eigentlich nur 10 Zeichen enthält, sollte auch zu denken geben.

Aufgrund der Fehlermeldung tippe ich jetzt mal darauf, dass die Quelltextdatei UTF-8 kodiert ist, aber einen Kodierungskommentar mit latin-1 oder ISO-8859-1 besitzt bzw. das aus irgend einem Grund angenommen wird.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Naja, in der ersten Zeile steht natürlich:

Code: Alles auswählen

# -*- coding: iso-8859-1-*- 
Das hatte ich jetzt weggelassen.

Die Quelltextdatei erstellt habe ich mit MyEclipse, habe dort aber bisher noch nirgends gesehen, dass ich das Coding irgendwo einstellen kann und hab da auch noch nie was daran geändert... :S
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Nutzte auch Eclipse. Man kann ein Encoding angeben. Ich meine es steht auf UTF-8

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

ah ... okay... habs gefunden ...

steht was von cp1252, also hab ich das coding in der ersten Zeile jetzt auch daraugf gesetzt. Gibt nun zumindest keinen Fehler mehr beim ß mehr.

Was allerdings bleibt sind die hyroglypen in der Datenbank, nur dass JETZT sogar 2 Zeichen pro Umlaut beansprucht werden.

:(
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

meneliel hat geschrieben:2 Zeichen pro Umlaut
So macht es UTF-8 nunmal ;) Je nach unicode Zeichen wird es mit bis zu 4 Zeichen gespeichert, siehe: http://de.wikipedia.org/wiki/Utf8

von [wiki]Unicode#UmgangMitDaten[/wiki]:
Der beste Weg, mit Daten umzugehen, ist der folgende:

* möglichst früh die externen Daten in Unicode wandeln
* intern alle Daten in Unicode belassen
* erst bei der Ausgabe von Daten von Unicode in das Ziel-Encoding wandeln
In deinem Fall heißt das die csv Daten in unicode wandeln und der Datenbank als UTF-8 übergeben.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

auch das probiert ....
also py-Datei oben cp1252 angegeben.
csv-Datei als iso-8859-1 geöffnet
in unicode gewandelt, danach in utf-8 ...

Wann und wie steht weiter oben in dem Thread.

nun seh* ich 4 Zeichen je Umlaut, aber immer noch keine Umlaute :(


* (im SQL-Developer und in ArcGIS, welches die Daten später verarbeitet.)
Antworten