Textdateien zeilenweise auslesen ja! Als Ganzes nein?

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

Python 2.6.5
***************

Hallo,

für das folgende Problem habe ich hier im Forum und auch im Internet herumgesucht, aber bisher keine Lösung gefunden (mag sein, dass ich ein zu grosser Python Newbie bin):

Wie kann ich eine utf-8-Textdatei, die deutsche Umlaute enthält nicht nur zeilenweise mit readline() (damit klappt's) sondern in einem Rutsch mit readlines() (gibt nur Codierungen) auslesen?

Das Programm:

Code: Alles auswählen

# filesSimple_3.py
# liest zeilen aus einer textdatei mit deutschen umlauten
# liest die zeilen nacheinander mit readline() klappt! 
# liest dann die zeilen alle mit readlines(): jein, nur codiert
# python 2.6
# 11-nov-2010

import codecs

# lies die zeilen aus notes.txt nacheinander mit readline()
print "Jetzt erst mal readline()"
print
my_file = codecs.open("notes.txt", "r", encoding="utf-8")
for i in range(0, 4):
	line = my_file.readline()
	print line,
print
my_file.close()

raw_input("hit ENTER to start readlines()")
print
# jetzt lies die zeilen aus notes.txt alle mit readlines()
print "Jetzt kommt readlines()"
print
my_file = codecs.open("notes.txt", "r", encoding="utf-8")
lines = my_file.readlines()
print lines
my_file.close()

print "das war readlines()"
So sieht die Ausgabe aus:
Jetzt erst mal readline()

Stelle das Ührchen
Ärgere dich nicht
Geh' nicht öffentlich
Meide heiße Speisen

hit ENTER to start readlines()

Jetzt kommt readlines()

[u'Stelle das \xdchrchen\n', u'\xc4rgere dich nicht\n', u"Geh' nicht \xf6ffentlich\n", u'Meide hei\xdfe Speisen\n', u'\n', u'\n']
das war readlines()
Und das ist die utf-8 Datei "notes.txt":
Stelle das Ührchen
Ärgere dich nicht
Geh' nicht öffentlich
Meide heiße Speisen
Ich bin dankbar für Tipps!
Gruss,

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

Vergiss, dass es readline() überhaupt gibt!

Du suchst das codecs-Paket:

Code: Alles auswählen

import codecs
with codecs.open(filename, "r", encoding="utf-8") as infile:
    data = infile.read()
Generell solltest Du Dateien immer mit "with" öffnen. Damit ist sichergestellt, dass die Datei auf jeden Fall geschlossen wird, auch wenn ein Fehler auftritt.

Edit: Sehe grad, dass Du das codecs-Paket schon gefunden hattest! Wo liegt dann das Problem?

Edit2: *seufz* jetzt hab ichs gesehen. Du gibst beim 2. Beispiel eine Liste aus. Dabei greift Python intern auf die __repr__()-Methode der in einer Liste gespeicherten Objekte zurück. Diese escapen die Umlaute eben entsprechend. Beim normalen "print" wird eben die __str__()-Methode aufgerufen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Danke erst mal!

Aber deine Bemerkung
Hyperion hat geschrieben:...beim 2. Beispiel eine Liste aus. Dabei greift Python intern auf die __repr__()-Methode der in einer Liste gespeicherten Objekte zurück. Diese escapen die Umlaute eben entsprechend. Beim normalen "print" wird eben die __str__()-Methode aufgerufen.
macht mich jetzt aber sehr :( Das heisst also auch, dass ich mit deutschen Umlauten nicht picklen kann, oder besser den Pickle nicht vernünftig ausgeben? Beim Unpicklen bekomme ich auch, so ähnlich wie oben:
recovered_list: ['\xc3\xa4rger', 73, 'hei\xc3\x9f', 8.1987599999999997e-12]
So sieht meine Anfängerübung im Picklen aus:

Code: Alles auswählen

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

import pickle
#
# pickle file
my_list = ['ärger', 73, 'heiß', 81.9876e-13]
print "usprüngliche my_list:", my_list
print
raw_input('hit ENTER to see recovered list')
pickle_file = open('my_pickled_list.pkl', 'w')
pickle.dump(my_list, pickle_file)
pickle_file.close()
print

# unpickle file
pickle_file = open('my_pickled_list.pkl', 'r')
recovered_list = pickle.load(pickle_file)
pickle_file.close()

print 'recovered_list:', recovered_list

Gibt es keine Möglichkeit, ein deutsches Pickle vernünftig auszugeben? Mit codecs.open() bin ich nicht zurechtgekommen. Muss ich beim Picklen wirklich auf deutsche Umlaute verzichten?
Gruss,

siggi
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nein, das Problem ist, dass `print liste` zwar `__str__` von `liste` aufruft, aber fuer dessen Elemente nur `__repr__` benutzt.

Code: Alles auswählen

>>> a = ["äö", "ßü"]
>>> print "[{0}]".format(", ".join(str(x) for x in a))
[äö, ßü]
>>> print a
['\xc3\xa4\xc3\xb6', '\xc3\x9f\xc3\xbc']
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Das ist mir etwas zu hoch. Ich hab das jetzt so verstanden: in Python keine Listen benutzten und alles, was mit Listen zu tun hat, wenn Umlaute und ähnliches darin vorkommen. Ist also auch nichts mit Picklen :( Werden denn mit dem neuen Python3 endlich mal auch Nicht-Amerikaner/Engländer/Australier/usw. zu ihrem Recht kommen?
Gruss,

siggi
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

siggi hat geschrieben:Ich hab das jetzt so verstanden: in Python keine Listen benutzten und alles, was mit Listen zu tun hat, wenn Umlaute und ähnliches darin vorkommen.
Nein. Das ist komplett falsch. Du kannst nur einfach nicht direkt `print` nutzen.
Vielleicht wirds dir damit klarer, dass es funktioniert:

Code: Alles auswählen

>>> a = ["äö", "ßü"]
>>> for x in a:
...     print x
... 
äö
ßü
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Es gibt von Objekten immer zwei Repräsentationen, einmal __repr__ und einmal __str__, wenn du eine Liste einfach nur ausgibst wird automatisch die __repr__ Methode zur Repräsentation genommen.
Damit ein Unicodeobject jedoch "schön" aussieht musst du die __str__ Methode rufen und das geht am einfachsten mit der build-in Funktion str().

"Nicht-Amerikaner/Engländer/Australier/usw" - wieso Engländer, aber nicht Schotten und Waliser ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Und was ist mit den Iren? Immer diese Ausgrenzungen :cry:
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Wollte sie schon dazu schreiben, dann hätte ich aber alle anderen auch noch aufzählen müssen, mir ging es nur um die eine Insel :D
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Xynon1 hat geschrieben:Es gibt von Objekten immer zwei Repräsentationen, einmal __repr__ und einmal __str__, wenn du eine Liste einfach nur ausgibst wird automatisch die __repr__ Methode zur Repräsentation genommen.
Damit ein Unicodeobject jedoch "schön" aussieht musst du die __str__ Methode rufen und das geht am einfachsten mit der build-in Funktion str()
Wir reden nicht von Python3. `unicode` statt `str` is da besser geeignet, sonst passiert das:

Code: Alles auswählen

>>> a = u"ä"
>>> str(a)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 0: ordinal not in range(128)
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Xynon1 hat geschrieben:Wollte sie schon dazu schreiben, dann hätte ich aber alle anderen auch noch aufzählen müssen, mir ging es nur um die eine Insel :D
Die Iren und alle anderen sind im usw. versteckt. Ich wollte die Waliser, Schotten und Cornish nicht so nahe and die Engländer bringen :wink: Jetzt aber im Ernst. Ich hab's jetzt so probiert, mit str():

Code: Alles auswählen

# unpickle file
pickle_file = open('my_pickled_list.pkl', 'r')
recovered_list = pickle.load(pickle_file)
s=str(recovered_list)
pickle_file.close()

print 'recovered_list:', s
Da ändert sich aber nichts. Trotzdem unschöne Ausgabe!
Gruss,

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

siggi hat geschrieben:Das ist mir etwas zu hoch. Ich hab das jetzt so verstanden: in Python keine Listen benutzten und alles, was mit Listen zu tun hat, wenn Umlaute und ähnliches darin vorkommen. Ist also auch nichts mit Picklen :( Werden denn mit dem neuen Python3 endlich mal auch Nicht-Amerikaner/Engländer/Australier/usw. zu ihrem Recht kommen?
Das klappt auch alles schon wunderbar mit Python2 ;-)

Noch mal ergänzend zu cofis Beiträgen:

Code: Alles auswählen

In [13]: data = ["äö", "ßü"]

In [14]: data
Out[14]: ['\x84\x94', '\xe1\x81']

In [15]: import pickle

In [16]: with open("test.pkl", "wb") as outfile:
   ....:     pickle.dump(data, outfile)
   ....:
   ....:

In [17]: with open("test.pkl", "rb") as infile:
   ....:     loaded_data = pickle.load(infile)
   ....:
   ....:

In [18]: loaded_data
Out[18]: ['\x84\x94', '\xe1\x81']

In [21]: for item in loaded_data:
   ....:     print item
   ....:
   ....:
äö
ßü
Wenn Du mir sofort gesagt hättest, dass Du etwas "picklen" willst, hätte ich Dir auch nicht zu codecs geraten, da pickle binäre Dateien schreibt. Daher das "b" als Zusatz beim modus zum lesen / schreiben von dateien. Bei binären Dateien spielt die Codierung beim Schreiben / Lesen erst einmal keine Rolle, da die Zeichen da noch nicht interpretiert werden!

Willst Du die Daten z.B. mit JSON speichern, passt der Hinweis zum codecs-Modul wieder btw.

Wo liegen / lagen denn jetzt Deine Verständnisprobleme?

Man muss doch eigentlich nur wissen, dass es zwei (eigentlich drei) Methoden für Objekte gibt, die eine druckbare Repräsentation eines Objektes zurückliefern. Einmal __repr__(), welches nur ASCII-Zeichen enthält und somit immer ohne Probleme in allen Shells und Umgebungen druckbare Zeichen liefert. Genau das wird dann innerhalb des Listenobjektes benutzt und deswegen werden damit Deine Zeichen entsprechend "verunstaltet". Das was Du eigentlich willst, ist die __str__()-Methode. Diese gibt bei einer Zeichenkette eben genau die Bytes zurück, die in dem String stehen. Genau das erreichst Du über die for-Schleife (Schritt 21), oder eben eleganter über die join()-Methode, wie von cofi bereits demonstriert:

Code: Alles auswählen

In [24]: print ",".join(loaded_data)
-------> print(",".join(loaded_data))
äö,ßü
Generell solltest Du Dich mal mit Zeichencodierungen und Unicode befassen. In Python2 sollte man intern immer mit Unicode arbeiten, was bedeutet, dass man möglichst früh bei Input und möglichst spät bei Output-Operationen eine Umwandlung von einer Byte-Codierung (wie "utf-8", "latin-1", usw.) bzw. von Unicode in eine Codierung vornehmen sollte.

Gute Erklärungen findest Du hier:
http://wiki.python-forum.de/Von%20Umlau ... 0Encodings

Und die Folien von Leonidas:
http://wiki.python-forum.de/User%20Grou ... folien.pdf

Ich hoffe das war jetzt hilfreich.

Kleiner Tipp noch: Für solch "banale" Sachen, solltest Du Dir angewöhnen, wie ich eine Shell fürs Testen zu benutzen. Damit kannst Du wunderbar solche kleinen Dinge testen und ein wenig rumprobieren.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Hyperion hat geschrieben:...

Code: Alles auswählen

...
for item in loaded_data:
     print item
...
:idea: , also Code geändert:

Code: Alles auswählen

# unpickle file
pickle_file = open('my_pickled_list.pkl', 'r')
recovered_list = pickle.load(pickle_file)
pickle_file.close()

print 'recovered_list:', 
for x in recovered_list:
	print x,
print
Wow! Gibt tolles Ergebnis :D

Danke!
Gruss,

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

Hm... das ist alles, was Dir zu den Postings einfällt? Ich hätte ja erwartet, dass Du nicht blindlings etwas aus Code-Schnipseln rauskopierst, sondern es versuchst nachzuvollziehen. Speziell der Teil mit dem "with" hätte Dich ja wenigstens stutzig machen können! Und auch am Modus hast Du nichts geändert - dabei bin ich darauf ja sogar explizit eingegangen... naja, viel Spaß noch beim "Trial and Error" :twisted:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Hyperion hat geschrieben:Hm... das ist alles, was Dir zu den Postings einfällt?...
Nimm's nicht tragisch, ihr habt mir ja hier geholfen! Übrigens "trial und error": ich habe gerade herausgefunden, dass alles mit Python3 und Umlauten ganz einfach geht, keine codecs, die Zeile:

Code: Alles auswählen

# -*- coding: utf-8 -*- 
wird nicht mehr gebraucht, und das Auslesen geht jetzt kinderleicht:

Code: Alles auswählen

# unpickle file
pickle_file = open('my_pickled_list.pkl', 'rb')
recovered_list = pickle.load(pickle_file)
:)

Bloß schade, dass Python3 mit Pythoncard nicht kompatibel ist, und noch nicht vollständig mit Pygame compatibel:
http://www.pygame.org/wiki/FrequentlyAs ... ython%203? :(
Gruss,

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

siggi hat geschrieben: Nimm's nicht tragisch, ihr habt mir ja hier geholfen! Übrigens "trial und error": ich habe gerade herausgefunden, dass alles mit Python3 und Umlauten ganz einfach geht, keine codecs, die Zeile:

Code: Alles auswählen

# -*- coding: utf-8 -*- 
wird nicht mehr gebraucht,
Aber Du hast vermutlich nicht kapiert, wieso ;-)
siggi hat geschrieben: ... und das Auslesen geht jetzt kinderleicht:

Code: Alles auswählen

# unpickle file
pickle_file = open('my_pickled_list.pkl', 'rb')
recovered_list = pickle.load(pickle_file)
:)
Allerdings ist es auch hier sinnvoll, das "with open() as _"-Idiom zu nutzen, wie nun schon mehrfach erwähnt! Und wo liegt der Unterschied zu der von mir gezeigten Variante in Python2? Ich sehe da keinen ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

Hyperion hat geschrieben:Und wo liegt der Unterschied zu der von mir gezeigten Variante in Python2? Ich sehe da keinen ;-)
Er hat zum ersten Mal die Datei im binären Schreibmodus geöffnet :mrgreen:
siggi
User
Beiträge: 79
Registriert: Montag 29. Januar 2007, 14:22

Sorry, war vorhin unvollständig, es muss so lauten:

Code: Alles auswählen

# unpickle file
pickle_file = open('my_pickled_list.pkl', 'rb')
recovered_list = pickle.load(pickle_file)
pickle_file.close()
print('print recovered_list:')
print(recovered_list)
Der loop mit Python2

Code: Alles auswählen

print 'recovered_list:', 
for x in recovered_list:
	print x,
ist mit Python3 nicht mehr nötig :D
Gruss,

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

siggi hat geschrieben:Der loop mit Python2

Code: Alles auswählen

print 'recovered_list:', 
for x in recovered_list:
	print x,
ist mit Python3 nicht mehr nötig :D
Sowas ist auch in Python 2.x nicht "nötig".
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

Für _diese_ Ausgabe schon:

Code: Alles auswählen

%> python
Python 2.6.6 (r266:84292, Oct  9 2010, 12:24:52) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print [u"äü", u"ßö"]
[u'\xe4\xfc', u'\xdf\xf6']

Code: Alles auswählen

%> python3
Python 3.1.2 (release31-maint, Oct 12 2010, 16:06:14) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print(["äü", "ßö"])
['äü', 'ßö']
Antworten