Dateioperationen mit Python

Kurze Einführung

Arbeitet man mit Daten, so ist es sinnvoll diese in Dateien abzuspeichern. So bleiben sie auch nach dem Ausschalten des Computers erhalten. Heutige eingebettete Systeme wie der Raspberry Pi besitzen meist eine SD-Karte auf der Dateien abgelegt werden können oder eine USB-Schnittstelle die es ermöglicht Dateien auf einem USB-Stick abzulegen. Mit solchen Systemen lassen sich dann zum Beispiel leicht, mit Hilfe von Python, die Daten eines angeschlossenen Sensors loggen. Sind Daten in Dateien abgespeichert, so müssen sie meist später noch verarbeitet werden. Auch dies kann meist mit einigen Zeilen Python-Code erledigt werden.

In Linux werden auch Geräte wie Dateien angesprochen. Die hier erlernten Kenntnisse werden also auch benötigt um auf Geräte zuzugreifen (zum Beispiel die serielle Schnittstelle).

Dateien öffnen, lesen und schließen

Mit der Methode open() wird eine Dateiobjekt erstellt (oft mit dem Namen f für file) , die Datei geöffnet, und die Datei dem Dateiobjekt zugewiesen. Die Mehode read() des Dateiobjekt liest dann die Datei ein.
Wird eine geöffnete Datei nicht mehr benötigt, sollte sie immer sofort mit der Methode close() geschlossen werden um den Speicher wieder frei zu geben und anderen Programmen den Zugriff wieder zu erlauben.

Achtung: Bei den Methoden die Klammern nicht vergessen!

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # file_open.py

    f = open("akkudata.dat")
    data = f.read()
    print (data)
    f.close()

Ausgabe der Datei:

29.10.13    09:09:58    1.81   22.87   0.0150833333 41.39     0.34
29.10.13    09:10:28    1.81   22.95   0.0301666667 41.54     0.69
29.10.13    09:10:58    1.8    23.02   0.0451666667 41.44     1.04
29.10.13    09:11:28    1.8    23.1    0.0601666667 41.58     1.38
29.10.13    09:11:58    1.81   23.16   0.07525      41.92     1.73
...

Der Nachteil dieser Methode ist, dass bei großen Dateien viel Speicher für das Variablenobjekt (hier mit dem (beliebigen) Namen data) benötigt wird.

Aufgabe F1:

Lade die Datei akkudata.dat herunter und speichere sie in das Verzeichnis deiner Python Programme. Teste dann das obige Programm in einem Terminalfenster (python3 file_open.py).

IO-Fehler abfangen:

Wurde die Datei nicht gefunden, so erhalten wir folgende Fehlermeldung:

FileNotFoundError: [Errno 2] No such file or directory: 'akkudata.dat'

Um dies zu vermeiden werden wir den Fehler mit der try...except-Anweisung abfangen. So können wir die Ausnahme (exception) mit einer eindeutigen Fehleraussage sauber dokumentieren. Mit der Methode exit() kann man das Programm dann verlassen. Die Datei muss hier nicht geschlossen werden, da sie ja nicht geöffnet wurde.
Es macht Sinn Dateinamen am Anfang des Programms einer Variablen zuzuweisen. So lassen sie sich später leichter ändern.

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # file_open2.py

    filename = "akkudata.dat"

    try:
        f = open(filename)
    except IOError:
        print('Cannot find file:',filename)
        exit()

    data = f.read()
    print (data)    
    f.close()
Aufgabe F2:

Teste das Programm der ersten Aufgabe, indem du es mit einem falschen Dateinamen aufrufst. Erweitere dann dein Programm um die Fehlerbehandlung mit try...except. Teste das Programm dann noch einmal mit richtigem und falschem Dateinamen.

Dateien schreiben

Daten werden mit der Methode write() geschrieben. Beim Öffnen der Datei muss man zusätzlich angeben, in welchem Modus die Datei geöffnet werden soll. Dabei gibt es folgende Möglichkeiten:

r (read)
w (write) Ersetzt den Inhalt einer bestehenden Datei. Falls keine Datei existiert wird sie erzeugt.
a (append) Hängt den Inhalt ans Ende einer bestehenden Datei an.
r+ Öffnet Datei zum Lesen und Schreiben (wird nicht oft benutzt).
w+ Öffnet Datei zum Lesen und Schreiben. Existiert die Datei noch nicht, so wird sie erzeugt.
a+ Öffnet Datei zum Lesen und Anhängen neuer Daten.

Gibt man den Parameter nicht an, so wird die Datei (wie in unserem Beispiel) nur zum Lesen geöffnet.

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # file_write.py

    filename = 'test.txt'

    try:
        f = open(filename,'w')
    except IOError:
        print('Cannot create file:',filename)
        exit()

    for i in range(10):
        f.write('Dies ist die '+str(i)+'te Zeile\n')    
    f.close()

    ###############

    # print the file for verification
    try:
        f = open(filename)
    except IOError:
        print('Cannot find file:',filename)
        exit()

    data = f.read()
    print (data)    
    f.close()

Der zweite Teil des Programms dient nur dazu sich die neu erzeugte Datei anzusehen, ohne sie mit dem Dateimanager öffnen zu müssen.

Aufgabe F3:

Teste das obige Programm. Ändere den Parameter w (write) nach a (append) und teste das Programm.

Daten in Dateien verändern

Die Datei im ersten Beispiel ist sehr groß. Dadurch muss das Variablenobjekt data viel Speicher in Anspruch nehmen. Dies kann bei noch größeren Dateien und eingebetteten Systemen mit wenig RAM zum Problem werden.
Außerdem braucht Python viel Zeit um alle Daten einzulesen bevor eine Operation mit den Daten durchgeführt wird. Besser ist es die Daten zeilenweise in einer Schleife zu verarbeiten:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # file_open3.py

    filename = 'akkudata.dat'

    try:
        f = open(filename)
    except IOError:
        print('Cannot find file:',filename)
        exit()

    line = f.readline()
    while line != '':
        print (line, end='')   
        line = f.readline()
    f.close()

Der zusätzliche Parameter end='' in der Print-Anweisung verhindert, dass print() eine zusätzliche Leerzeile ausgibt.

Die verwendete Datei enthält Daten die beim Laden eines Akkus aufgenommen wurden. Die Daten sind durch ein Tabulatorzeichen (ASCII 0x09, '\t') getrennt und jede Zeile endet mit dem Linefeed-Zeichen (LF, ASCII 0x0A, '\n') wie in Linux üblich. Die Datei soll in einer anderen Software unter Windows weiterverarbeitet werden. Dazu sind folgende Änderungen nötig: Der Punkt soll durch ein Komma-Zeichen ersetzt werden, der Datum-Punkt durch einen Schrägstrich ('/') und das Newline-Zeichen am Ende soll durch einen zusätzlichen Wagenrücklauf (carriage return, CR, ASCII 0x0D, '\r') erweitert werden, da Windows beide Zeichen erwartet (siehe http://en.wikipedia.org/wiki/Newline). Dazu wird eine zweite Datei geöffnet, welche dann nach dem Schreiben die veränderte Datei enthält. Das entsprechende Programm kann dann folgendermaßen aussehen:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # file_change.py
    filename1 = 'akkudata.dat'
    filename2 = 'akkudata_corr.dat'

    try:
        f1 = open(filename1)
    except IOError:
        print('Cannot find file:',filename1)
        exit()
    try:
        f2 = open(filename2,'w')
    except IOError:
        print('Cannot create file:',filename2)
        exit()

    line = f1.readline()
    while line != '':
        line=line.replace('.','/',2)    # replace two date points with slashes
        line=line.replace('.',',')      
        line=line.replace('\n','\r\n')  # replace Linefeed with CR+LF
        f2.write(line)    
        print (line, end='')   
        line = f1.readline()    
    f1.close()
    f2.close()
Aufgabe F4:

Schreibe ein Programm das die GUI Tkinter nutzt und es erlaubt die Punkte in einer Datei durch Kommas zu ersetzen oder umgekehrt (ttk.Radiobutton). Die beiden Dateinamen sind durch das Widget ttk.Entry veränderbar.

comma converter

Zusatzaufgabe F5:

Da die originale Daten-Datei (Laden eines Akkus) sehr groß ist, soll über jeweils 10 Werte ein Mittelwert gebildet werden. Auch sollen nur Strom und Spannung (dritte und vierte Kolonne) verwendet werden. Schreibe das entsprechende Programm (ohne GUI). Die neue Datei soll akkudata_short.dat heißen.

Tipp: Mit der Methode split() kann man die Zeile aufspalten (hier beim Tabulator) und in eine Liste umwandeln.

    spline=line.split('\t')

Auf den Strom kann dann zum Beispiel mit spline[2] zugegriffen werden.