Umlaute umsetzen

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
oshoki
User
Beiträge: 30
Registriert: Donnerstag 3. Januar 2008, 22:49

Hallo zusammen,
es gibt schon viele Beiträge zum Umlaut Thema, doch leider habe ich es noch nicht so richtig verstanden, wie es funktioniert. Als Anfänger habe ich da wohl so meine Anlaufschwierigkeiten.

Ich möchte gern eine Datei mit csv einlesen und den Inhalt der Datei, die auch Umlaute beinhalten kann letztendlich mit sql in eine Datenbank schreiben.
Leider scheitert es jetzt schon daran, dass ich die Umlaute nicht lesbar in meiner Liste dargestellt bekomme. Wenn ich utf-8 in latin-1 wechsle, dann wird es "besser" aber noch nicht gut. Wie funktioniert es denn?

So sieht es momentan aus. Ich sitze fest und freue mich auf jede Lösung, wenn ich sie auch verstehe:

Code: Alles auswählen

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

import codecs
import csv

"""
umlaut.dat
Erste Zeile;Ein Wort mit kleinem ö, wie Möwe;Ein Wort mit ß, wie es in groß steht;Ein Satz mit ü, wie die Kuh macht muh,viele Kühe machen Mühe;
Zweite Zeile;Ein Wort mit großem Ö, wie Ödipus;Ein Wort mit großem Ä, wie Ärger;Ein kleines ä steht in ärgerlich;
"""

fi = open("umlaut.dat",'r')

reader = csv.reader(codecs.EncodedFile(fi, 'utf-8', 'latin-1'), delimiter=';', quoting=csv.QUOTE_NONE)

data = list(reader)

print data

"""
Ergebnis:
[['Erste Zeile', 'Ein Wort mit kleinem \xc3\xb6, wie M\xc3\xb6we', 'Ein Wort mit \xc3\x9f, wie es in gro\xc3\x9f steht', 'Ein Satz mit \xc3\xbc, wie die Kuh macht muh,viele K\xc3\xbche machen M\xc3\xbche', ''], ['Zweite Zeile', 'Ein Wort mit gro\xc3\x9fem \xc3\x96, wie \xc3\x96dipus', 'Ein Wort mit gro\xc3\x9fem \xc3\x84, wie \xc3\x84rger', 'Ein kleines \xc3\xa4 steht in \xc3\xa4rgerlich', '']]
"""
Edit (Leonidas): Code in Python-Tags gesetzt.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Das liegt daran, dass die Elemente einer Liste durch print in ihrer ASCII-Repräsentation dargestellt werden.

Versuch mal

Code: Alles auswählen

print data[1][1]
print repr(data[1][1])
Und bitte nimm beim nächsten Mal Code-Tags.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Du sagst, deine Eingabedatei ist im UTF-8-Format. Unter *nix/OS X kannst du das z.B. mit `od` prüfen:

Code: Alles auswählen

$ cat a.txt
ä;ö;ü
a;o;u
$ od -tx1 a.txt
0000000    c3  a4  3b  c3  b6  3b  c3  bc  0a  61  3b  6f  3b  75  0a
`c3 a4` ist ein Umlaut, das `3b` ist das Semikolon... also alles prima.

So eine Datei musst du jetzt natürlich binär laden, nicht wie du es machst als Textdatei. Desweiteren willst du von UTF-8 in Latin-1 (ISO-8859-1) umkodieren, nicht anders herum, wie du es angibst.

Wenn du `open("a.txt", "rb")` und `read()`benutzt, erhältst du einen Byte-String, der die selben Bytes enthält und genauso lang ist, wie die Ausgabe von `od`.

Code: Alles auswählen

>>> s = open("a.txt", "rb").read(); s
'\xc3\xa4;\xc3\xb6;\xc3\xbc\na;o;u\n'
Diesen Byte-String könntest du mit `s.decode("utf-8")` zu einem Unicode-String machen, den du dann auf der Console auch korrekt mit print ausgeben kannst. Dies kann auch, muss aber nicht, mit dem Byte-String funktionieren.

Code: Alles auswählen

>>> u = s.decode("utf-8"); u
u'\xe4;\xf6;\xfc\na;o;u\n'
Das `\xe4` ist Pythons Darstellung des Unicode-Codepoints `u+00e4`, "LATIN SMALL LETTER A WITH DIAERESIS", also dem kleinen A-Umlaut. Dieser entspricht zufällig auch Bytewert des Zeichens in ISO-8859-1-Kodierung, darf aber nicht verwechselt werden. Das die Repräsentation eines Unicode-Strings nur Zeichen aus dem ASCII-Subset von Unicode direkt anzeigt und den Rest als hexadezimale Codepoints ist eine Design-Entscheidung von Python.

Ebenso ist es eine, dass der CSV-Reader offenbar nur mit Bytestrings klarkommt, nicht mit Unicode. Wenn man die Umlaute erhalten will, bietet sich wie von dir gewählt ISO-8859-x an (ich würde ja ISO-8859-15 vorschlagen, da dies das € erhält). Ich kann meinen Unicode-String also in einen Bytestring mit diesem Encoding verwandeln:

Code: Alles auswählen

>>> y = u.encode("latin-1"); y
'\xe4;\xf6;\xfc\na;o;u\n'
Das kann man jetzt in den CSV-Reader füttern.

Alles zusammen:

Code: Alles auswählen

>>> list(csv.reader(codecs.EncodedFile(open("a.txt", "rb"), "latin-1", "utf-8"), delimiter=";"))
[['\xe4', '\xf6', '\xfc'], ['a', 'o', 'u']]
Wenn du jetzt allerdings wieder wirklich mit den Strings arbeiten willst, würde ich sie nochmals in Unicode-Strings umwandeln.

Oder:

Wozu überhaupt diesen CSV-Reader, wenn du eh keinen Anführungszeichen brauchst und nur an einem ";" trennen willst. Das geht auch einfacher. Dazu nutze ich, dass `codecs.open` eine Text-Datei, die in einem bestimmten Encoding vorliegt, öffnen und automatisch in Unicode-Strings verwandeln kann:

Code: Alles auswählen

>>> [line.split(";") for line in codecs.open("a.txt", encoding="utf-8")]
[[u'\xe4', u'\xf6', u'\xfc\n'], [u'a', u'o', u'u\n']]
Um die Zeilenenden wegzubekommen und ggf. vorhandene Leerzeichen auszufiltern, muss man noch einmal `strip()` anwenden, also wird die Aufgabe zu folgendem Einzeiler:

Code: Alles auswählen

>>> [[col.strip() for col in row.split(";")] for row in codecs.open("a.txt", encoding="utf-8")]
[[u'\xe4', u'\xf6', u'\xfc'], [u'a', u'o', u'u']]
Stefan
oshoki
User
Beiträge: 30
Registriert: Donnerstag 3. Januar 2008, 22:49

Vielen Dank für die rasche und ausführliche Hilfe.
Ich denke, das decode und encode habe ich verstanden, wobei das encoding in codecs.open als decode (also Wandlung in unicode) verstanden werden muss.

Die zwei letzten verschachtelten Codezeilen finde ich super und das Ergebnis ist auch fantastisch. Leider verstehe ich es nicht so richtig und wüsste auch nicht wie ich es alternativ darstellen könnte.

Code: Alles auswählen

>>> [line.split(";") for line in codecs.open("a.txt", encoding="utf-8")]
[[u'\xe4', u'\xf6', u'\xfc\n'], [u'a', u'o', u'u\n']]
und

Code: Alles auswählen

>>> [[col.strip() for col in row.split(";")] for row in codecs.open("a.txt", encoding="utf-8")]
[[u'\xe4', u'\xf6', u'\xfc'], [u'a', u'o', u'u']]
Wenn mir da noch jemand etwas Hilfestellung geben könnte, wäre es super.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Das sind "list comprehensions", eine verkürzende Schreibweise für eine "traditionelle" for-Schleife. Ein

Code: Alles auswählen

result = [expr for var in listexpr if cond]
steht für

Code: Alles auswählen

result = []
for var in listexpr:
    if cond:
        result.append(expr)
Stefan
Antworten