Mit python 2 aus Datei mit Umlauten in Python-Liste lesen?

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.
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Hallo,

Ich habe Python 2.7.3 und Python 3.2.3 auf Ubuntu 12.04. Zum Testen lese ich eine kleine Textdatei in eine Liste. Mit Python 3 geht das ohne Probleme:

Code: Alles auswählen

#!/usr/bin/env python3

with open("wordsDE.txt") as my_file:
	lines = my_file.readlines() # read lines into a list
my_file.close()
print(lines)
Ausgabe:
  • ['ührchen\n', 'ähre\n', 'öffnen\n', '\n']
Leider nich so mit Python 2, trotz Codierens:

Code: Alles auswählen

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

import codecs

#my_file = open('wordsEN.txt', 'r') # ok
with codecs.open("wordsDE.txt", "r", encoding = "utf-8") as my_file:
	lines = my_file.readlines() # read lines into a list
my_file.close()
print lines
Ausgabe:
  • [u'\xfchrchen\n', u'\xe4hre\n', u'\xf6ffnen\n', u'\n']
Wie kriege ich es denn hin, dass auch mit Python 2 die Wörter so wie mit Python 3 in der Liste stehen?

Danke,

siggi
Gruss,

siggi
BlackJack

@siggi: Das geht so wie Du das gemacht hast beziehungsweise gar nicht. Du machst das so schon richtig in Python 2. Die Zeichenkettendarstellung von *Listen* sieht halt in den beiden Python-Versionen unterschiedlich aus. Das sollte Dich nicht weiter stören. Falls doch, dann machst Du da etwas was man in beiden Versionen nicht machen sollte. Listen ausgeben ist zum Debugging gedacht. Und da zeigt Python 2 eine Repräsentation von Unicode-Zeichenketten die sich auf ASCII-Zeichen beschränkt und alles ausserhalb als Escape-Sequenz anzeigt, damit man als Programmierer auch genau sieht welche Werte da enthalten sind, nahezu unabhängig von der Kodierung die man für die Anzeige verwendet. Da finde ich die Darstellung in Python 3 als Rückschritt.

Ausserdem musst Du in Python 3 auch eine Kodierung beim öffnen der Datei angeben, zumindest wenn das mit den selben Daten auf einem anderen System nicht potentiell auf die Nase fallen soll.
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

@BlackJack: in dem Fall nicht zum Debugging, sondern um aus der Datei - also aus der Liste - mit random.choice() zufällig ein Wort zur weiteren Verarbeitung auszuwählen. Das geht also nur mit Python 3?
Gruss,

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

siggi hat geschrieben:Das geht also nur mit Python 3?
Natürlich nicht! Lies Dir mal die Links in meiner Signatur durch; dann verstehst Du das Thema rund um Encodings und Unicode.

Du musst die Daten für die *Anzeige* entsprechend codieren. Unter Windows vermutlich cp-1252 als Encoding für die Ausgabe.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@siggi: Gib doch einfach mal so ein Wort aus. Ich denke Du siehst Probleme wo keine sind. Die beiden Listen enthalten in Python 2 und 3 im Grunde genau die gleichen Daten. Nur die Darstellung der *Liste* mit den Daten drin unterscheided sich. Wenn Dich diese Darstellung gar nicht interessiert, dann beachte sie einfach nicht.
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

@Hyperion, @BlackJack: nicht Windows, sondern ich benutze gerade Linux (Ubuntu). Ich bin aber schon etwas weiter gekommen:
Mit diesen Modifikationen werden Worte aus der Liste "richtig" ausgegeben:

Code: Alles auswählen

# -*- coding: utf-8 -*- 

import codecs
import random

#my_file = open('wordsEN.txt', 'r') # ok
with codecs.open("wordsDE.txt", "r", encoding = "utf-8") as my_file:
	lines = my_file.readlines() # read lines into a list
my_file.close()
print lines
currentword = random.choice(lines)
currentword = currentword.strip()  #strip newline off the end of the word

print currentword
Warum, ist mir nicht klar. Leider noch ein Schönheitsfehler: ab und zu wird "nichts"* ausgegeben, statt eines der Wörter aus der Liste. Liegt das daran, dass nach dem letzten Wort in der Datei 3-mal hex"0A" steht? Wenn ja, wie kriege ich das weg?

Edit: *: nicht das Wort nichts, sondern wirklich nichts
Gruss,

siggi
BlackJack

@siggi: Das explizite schliessen einer Datei wird mit dem ``with``-Block unnötig — deswegen verwendet man den ja.

Ich weiss nicht wie man das besser klar machen als einfach zu sagen: Die Zeichenkettendarstellung einer Liste wird in Python 2 immer nur Zeichen im ASCII-Bereich produzieren. Zeichenketten und Unicode-Objekte in der Liste werden dabei so kodiert das alle Zeichen ausserhalb als Escape-Sequenzen kodiert sind. Das ist halt so. Um dem Programmierer genau zu zeigen was in den Objekten für Daten stehen egal welche Kodierung das Ausgabemedium, der Editor, oder wo immer man sich diese Listendarstellung anschaut für eine Kodierung verwendet. Das ist unglaublich sinnvoll wenn man Probleme mit der Kodierung in Bytestrings hat, eben weil man *genau* sieht welche Bytewerte enthalten sind, ohne dass das noch einmal von irgendeiner Software als Sonderzeichen interpretiert wird und man wissen müsste *wie* das interpretiert wird von dieser Software.

Das mit den Hexwerten ist auch wieder eine Darstellungssache, denn da steht nicht dreinmal 0A sondern dreimal ein Newline-Zeichen am Ende und irgendeine Software (welche?) zeigt Dir das so an. Der Bytewert 0A ist das newline-Zeichen, also ein Zeilenende (unter Unix), also je nach dem wo Du mit dem zählen begonnen hast enthält die Datei am Ende zwei bis drei Leerzeilen. Und wenn die ausgewählt werden, wird natürlich „nichts” ausgegeben.
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Habe kurz gegooglet: hex0A ist ASCII code 12, also Linefeed. 3 mal Linefeed am Ende der Datei! Ein bißchen viel!
Gruss,

siggi
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@siggi: das sind genau so viele Leerzeilen, wie derjenige, der die Datei erzeugt hat, da reingeschrieben hat. Wenn Du keine Leerzeilen in Deiner Liste haben willst, mußt Du sie herausfiltern.
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

@BlackJack: danke für deine Erläuterungen. Es war ghex und hexdump zum Binärlesen.

Die vielen ASCII 12 Befehle habe ich jetzt mit pop() weggekriegt. Wieder was gelernt

Mann, ist das kompliziert :evil:
Gruss,

siggi
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

Schön, dass Du etwas gelernt hast. In diesem Fall ist aber »pop« der falsche Weg.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Du hast ja schon festgestellt, dass die Dateien nicht so aussehen wie du dachtest oder wie du es dir gewuenscht hast.
Wenn das schon zutrifft, liegt es auch nahe dass die Dateien unregelmaessig sind (oder zumindest nicht deinen erwarteten Regeln entsprechen).

Ist es dann clever mittels `pop` die letzten 3 Zeilen zu entfernen ohne sie vorher zu ueberpruefen?

Alternative: Wir schauen uns die Zeilen an und behandeln nur die, die wir erkennen.

Code: Alles auswählen

def process_line(line):
    ...

def acceptable_line(line):
    return line != '\r'

with open(path) as f:
    for line in f:
        if acceptable_line(line):
            process_line(line)
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

cofi hat geschrieben:...Wir schauen uns die Zeilen an und behandeln nur die, die wir erkennen.
cofi, du "wirfst deine Perlen vor die Sau" ;-) will sagen, ich blicke da nicht durch.

Ich habe es jetzt so - ohne pop() - gelöst:

Code: Alles auswählen

# -*- coding: utf-8 -*- 

import codecs
import random

with codecs.open("wordsDE.txt", "r", encoding = "utf-8") as my_file:
	lines = my_file.readlines() # read lines into a list

print lines

currentword = random.choice(lines)
currentword = currentword.strip() 
while currentword == "":
	currentword = random.choice(lines)
	currentword = currentword.strip() 

print currentword
d.h. die letzte "leere" Zeile wird in der while-Schleife nicht berücksichtigt. Ist das jetzt auch falsch?
Gruss,

siggi
BlackJack

@siggi: Das kommt auf die Definition von falsch an. IMHO ja, denn es ist unnötig kompliziert, hat eine schlechtere Laufzeit weil man ja nicht unter Kontrolle hat wie oft unnötigerweise eine Leerzeile ausgewürfelt wird, und es werden Quelltextzeilen wiederholt hingeschrieben.

Du musst doch nur eine Liste erstellen bei denen die Leerzeilen nicht enthalten sind und schon gibt es das Problem nicht mehr. Es ist dann vor dem ziehen der Zufallszeile gelöst und nicht irgendwie umständlich danach. Ungetestet:

Code: Alles auswählen

import io
import random


def main():
    with io.open('wordsDE.txt', 'r', encoding='utf-8') as words_file:
        lines = filter(None, (line.strip() for line in words_file))
    print random.choice(lines)


if __name__ == '__main__':
    main()
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Danke, BlackJack!

das alles, was ich hier in meinem Thread bisher von euch gelernt habe, stand bisher in keinem meiner Anfängerbücher für Python. Ich muss das erst mal setzen lassen.

Übrigens, ich habe erst jetzt gemerkt, dass die "Leerzeilen" in der Datei words.txt zustandekommen, wenn ich nach der Eingabe des letzten Wortes unbeabsichtigt noch ein oder mehrere <Enter>s eingegeben habe. Das sehe ich nicht im Texteditor sondern nur mit LibreOffice Writer.
Gruss,

siggi
BlackJack

@siggi: Dann solltest Du vielleicht den Texteditor wechseln oder mal in den Einstellungen schauen wie man das sichtbar macht. Bei denen die ich so verwende sieht man es an den Zeilennummern und bei den meisten kann man „whitespace”-Zeichen auch sichtbar machen.
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

@BlackJack: Danke, das mit den Zeilennummern war ein guter Tipp!

Übrigens waren meine Fragen hier nur ein "Vorspiel" zum eigentlichen Problem: "Wie kriege ich Hangman für Python2-PyQt4 aus Hello World 2nd Ed.* dazu, deutsche Umlaute zu akzeptieren?" Nachdem ich http://blog.notdot.net/2010/07/Getting- ... -in-Python gelesen hatte und nach euerem Insistieren hier auf's richtige Coding, war's mir klar: die wichtigste Modifikation war, jedes Vorkommen von str( im Listing durch unicode( zu ersetzen.

*: listing_22-8.py aus ch22.zip von http://www.manning.com/sande/sourcecode ... y_Chapter/
Gruss,

siggi
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Bin ich froh, dass ich - was Python und Unicode betrifft - ein Deutscher bin und nicht ein Franzose oder Schwede oder... Denn:
Ein Python-Experte sagte mir, es könne sein, dass z.B.

"à" == "à" FALSE ergeben kann! Begründung: http://en.wikipedia.org/wiki/Precomposed_character

Dann wird's mit Hangman schwierig, weil die beiden identisch-aussehenden Buchstaben auf verschiedenem Weg entstanden sein könnten. Und bei ä, ü, ö, ß gibt es ja wohl nur eine Art, die Zeichen zu generieren.

Stimmt das?
Gruss,

siggi
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

Meinst Du sowas?

Code: Alles auswählen

>>>"ä"=="ä"
False
>>>print(ascii("ä"))
'a\u0308'
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Sirius3
Für was wird das denn verwendet? Und wie funktioniert das denn: Setze 2 Punkte über das Zeichen links vom Cursor. Ich dachte, Unicode sei "nur" eine große Tabelle mit allen möglichen und unmöglichen Zeichen dieser Welt. Aber bei '\u0308' wird ja nicht nur auf ein Zeichen verwiesen, sondern auch etwas gemacht. Wobei jetzt, wenn ich das hier so schreibe denke ich eher, dass es der Codec ist, der das, worauf '\u0308' verweist, "ausführt"... Uiuiui...

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