Dateien sind die einfachste Möglichkeit, Daten dauerhaft zu speichern, wie du das in Python programmatisch machst, zeigt dir dieser Teil der Reihe.
Dateien
Letztendlich wird alles in Dateien gespeichert, seinen es Bilder, Briefe, Tabellen oder auch die Konfigurationsdateien in /etc deines Raspberry Pi. Auch Webseiten, die du aufrufst liegen auf dem Server in einer Datei. Sehen wir mal, wie du in Python mit Dateien (oder englisch Files) arbeiten kannst.
Datei erzeugen
Eine Datei wird erzeugt, indem du sie mit der Python Funktion open()
öffnest. Die Funktion erwartet zwei Parameter, zum einen den Dateinamen, zum anderen den Modus, in dem die Datei geöffnet werden soll. Sie liefert einen sog. Filehandle zurück, den du in einer Variable ablegen solltest, um in die Datei zu schreiben oder aus ihr zu lesen.
f = open('datei.txt','x')
w | schreiben |
w+ | schreiben/lesen, überschreibt bestehende Datei oder legt neue Datei an |
wb+ | wie w+, binär |
r | lesen |
r+ | lesen und schreiben |
rb | lesend binär Modus |
rb+ | lesend/schreibend binär |
a | anhängen |
a+ | anhängen und lesen |
ab | anhängen, binär |
x | exclusive – Datei neu anlegen, Fehler, wenn Datei bereits existiert. |
Eine Datei ist eine Abfolge von Bytes auf dem Datenträger. Die Interpretation des Inhalts wird vom jeweiligen Dateiformat vorgeschrieben. Textdateien sind einfache eine Abfolge von Strings, die durch ein Newline ‚\n‘ von einander getrennt sind. Zu den Binärdateien zählen z.B. alle Bilddateien wie JPEG oder PNG, aber auch Videos und Audiofiles. python interpretiert jede Datei standardmäßig als Textdatei, außer, du sagst über den Dateimodus etwas anderes.
in Datei schreiben
Um eine Liste in eine Datei zu schreiben, öffnest du die Datei zunächst im Schreibmodus ‚w‘ iterierst dann über die Liste und schreibst jeden Eintrag mit f.write() in die Datei. Wichtig ist, die Datei am Ende mit f.close() zu schließen, um die Daten aus dem Zwischenspeicher des Betriebssystems wirklich auf den Datenträger zu schreiben.
namen = ['Homer','Marge','Bart','Lisa','Maggie']
f = open('namen.txt','w')
#f.write(namen)
for name in namen:
f.write(f'{name}\n')
f.close()
Damit wird eine Datei namen.txt
erzeugt, die wie folgt aussieht.Beachte, dass ein I/O-Fehler abgefangen wird und das Schließen der Datei im finally
Block auf jeden Fall durchlaufen wird.
Homer
Marge
Bart
Lisa
Maggie
Durch den f-String mit LineFeed (\n) wird jeder Name der Liste in eine eigene Zeile geschrieben.
Es gibt einen Kniff, diesen Code kürzer zu halten. Das with
Statement öffnet Dateien automatisch und schließt sie zum Ende des Blocks auch automatisch wieder.
from namen import namen
try:
with open('namen.txt','w') as f:
for name in namen:
f.write(f'{name}\n')
except IOError as err:
print(f'I/O-Fehler {err}')
Du siehst, dass nicht nur das open()
weggefallen ist sondern auch der gesamte finally
Block.
Datei lesen
Jetzt wollen wir unsere Datei auch wieder einlesen.
namen = []
try:
f = open('namen.txt','r')
#f = open('namen.txt','r',encoding='utf-8')
for line in f:
namen.append(line)
except IOError as x:
print(f'I/O-Fehler: {x}')
finally:
if f != None:
f.close()
print(namen)
Du öffnest die Datei Read-Modus und iterierst danach über jede Zeile in der Datei und fügst die Zeile zur Liste namen
dazu. Falls du Umlaute in der Datei hast, benutzt du die auskommentierte open()
Zeile, die das Encoding auf ‚UTF-8‘ setzt, dieser Parameter bei open()
ist optional. Die Ausgabe sieht dann so aus:
['Homer\n', 'Marge\n', 'Bart\n', 'Lisa\n', 'Maggie\n']
Das ist natürlich unschön bis störend, dass der Zeilenumbruch Teil des Namens ist.Um das zu beheben,musst du die Zeile namen.append()
wie folgt ersetzen:
namen.append(line.strip())
Wie du schon gesehen hast, entfernt die strip()
– Methode Leerzeichen und Zeilenumbruchzeichen am Stringende. Dadurch stehen dann nur noch die Namen ohne Zeilenvorschub in der Liste.
Kommaseparierte Dateien
Eines der einfachsten Verfahren, Daten strukturiert zu speichern, sind kommaseparierte Dateien (engl.: commaseperated values, CSV). In diesen Dateien werden tabellarische Daten gespeichert, jede Spalte der Tabelle wird mit Kommata voneinander getrennt, jede Tabellenzeile ist eine Zeile in der CSV Datei. im Repository ist die Datei namen.csv hinterlegt.
Skinner,Seymour
Largo,Dewey
Krabappel,Edna
Hoover,Elisabeth
Chalmers,Gary
Jetzt geht’s darum, diese Datei einzulesen und zu verarbeiten
school = []
class Lehrer():
def __init__(self, vorname, name):
self.vorname = vorname
self.name = name
def __str__(self):
return f'{self.vorname} {self.name}'
try:
with open('namen.csv','r') as f:
for line in f:
splitted = line.strip().split(',')
name = splitted[0]
vorname = splitted[1]
lehrer = Lehrer(vorname,name)
school.append(lehrer)
except IOError as x:
print(f'I/O-Fehler: {x}')
print(school)
print(school[0])
print(school[3])
Nach dem Entfernen des LineFeed mittels strip()
, wird die gelesene Zeile mittels split()
in seine einzelnen Werte aufgeteilt. split()
liefert eine Liste der Werte in der Zeile zurück, genau an den Kommata aufgetrennt. Da wir wissen, dass der Nachname in jeder Zeile der erste Wert ist, können wir ihn mit name = splitted[0]
auslesen. Ebenso der vorname
als zweiten Wert. Die Gefahr bei CSV Dateien liegt darin, dass die Datei nicht exakt so strukturiert ist, wie es dass Programm vorgibt. Die Folge ist dann meist ein unkontrollierter Programmabbruch und Verarbeitungsfehler. In unserem kleinen Beispiel besteht die Gefahr nicht und wir bauen mit vorname
und name
pro Zeile in der CSV-Datei eine Instanz der Klasse Lehrer
auf und fügen diese der Liste school
dazu.
XML lesen
Neben der nicht selbsterklärenden Struktur einer CSV-Datei gibt es bei CSV meist auch noch das Problem mit Werten, die das Trennsymbol enthalten (bspw. Beträge mit Nachkommastellen). Diese dann korrekt zu verarbeiten erfordert u.U. einigen Aufwand.
Mit XML (eXtensible Markup Language) wurde ein Dateiformat geschaffen, dass diese Probleme behebt. Die Struktur ist sowohl für Menschen als auch für Computerprogramme verständlich. Letztlich basiert XHTML, das die Struktur von Webseiten beschreibt, auch auf XML.
In der Datei school.xml
habe ich die obige CSV Datei als XML hinterlegt.
<schule>
<lehrerliste>
<lehrer>
<name>Skinner</name>
<vorname>Seymore</vorname>
</lehrer>
<lehrer>
<name>Largo</name>
<vorname>Dewey</vorname>
</lehrer>
<lehrer>
<name>Krabappel</name>
<vorname>Edna</vorname>
</lehrer>
<lehrer>
<name>Hoover</name>
<vorname>Elisabeth</vorname>
</lehrer>
<lehrer>
<name>Chalmers</name>
<vorname>Garry</vorname>
</lehrer>
</lehrerliste>
</schule>
Um die Datei zu verarbeiten, benötigen wir einen Parser. Zu unserem Glück bringt Python diesen bereits mit und wir müssen ihn nur noch importieren und benutzen.
import xml.etree.ElementTree as ET
Wir importieren die Klasse ElementTree
des Package xml.etree
und können sie unter dem Namen ET
im Programm ansprechen.
Diese Klasse stellt uns alles zur Verfügung, was das syntaktische Parsen der XML-Datei angeht. Um die smantische Auswertung müssen wir und selbst kümmern. Zunächst lesen wir die Datei ein und erhalten so einen Baum des Markups.
tree = ET.parse('./school.xml')
root = tree.getroot()
In tree
ist jetzt der gesamte geparste Baum der XML-Struktur eingelesen, root
enthält des Wurzelelement ( in unserer Datei <schule>
). Um über die Liste der <lehrer>
Elemente zu iterieren, lassen wir uns diese von root
geben:
for item in root.findall('lehrerliste/lehrer'):
Die findall()
Methode liefert uns alle Elemente im Wurzelelement zurück, die im Knoten „lehrerliste
“ liegen und ein <lehrer>
Element sind. Innerhalb von item
liefert uns die find() Methode die Elemente für <name> und <vorname>
vorname = item.find('vorname').text
name = item.find('name').text
Im text
Attribut findest du dann die Werte für Namen und Vornamen.
Der komplette Code sieht dann so aus:
import xml.etree.ElementTree as ET
class Lehrer():
def __init__(self, vorname,name):
self.vorname = vorname
self.name = name
def __str__(self):
return f'{self.vorname} {self.name}'
school = []
tree = ET.parse('./school.xml')
root = tree.getroot()
print(f'{tree}')
print(f'{root}')
for item in root.findall('lehrerliste/lehrer'):
vorname = item.find('vorname').text
name = item.find('name').text
print(f'item = {item}, {vorname} {name}')
school.append(Lehrer(vorname, name))
print()
print(school)
print(school[0])
print(school[3])
JSON lesen
Das Einlesen einer JSON-Datei (JavaScript Object Notation) sieht ziemlich ähnlich aus.
Zunächst die Datei school.json mit den Datensätzen:
{
"lehrerliste": [
{
"name": "Skinner",
"vorname": "Seymore"
},
{
"name": "Largo",
"vorname": "Dewie"
},
{
"name": "Krabappel",
"vorname": "Edna"
},
{
"name": "Hoover",
"vorname": "Elisabeth"
}
]
}
Das passende Programm sieht dem von XML sehr sehr ähnlich.
import json
json = json.loads(open('./school.json').read())
class Lehrer():
def __init__(self, vorname,name):
self.vorname = vorname
self.name = name
def __str__(self):
return f'{self.vorname} {self.name}'
school = []
liste = json['lehrerliste']
for item in liste:
name = item['name']
vorname = item['vorname']
school.append(Lehrer(vorname,name))
print()
print(school)
print(school[0])
print(school[3])
Das soll unseren Ausflug in die Dateien erstmal abschließen. Als Nächstes wollen wir mal auf Datumsangaben schauen.