Dateien schließen nach Dateifehler

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
niederrheiner
User
Beiträge: 30
Registriert: Montag 7. Januar 2013, 11:52

Hallo an Alle,
folgendes Problem:
am Anfang des Programmes öffne ich mehrere Dateien mit try, except und sys.exit(-1. Wenn also 3 Dateien erfolgreich geöffnet wurden und die vierte Datei einen Fehler wirft, so verlasse ich das Programm über sys.exit(-1) und die vorher geöffneten Dateien werden nicht sauber geschlossen.

Wie baue ich das Programm um, damit im Fehlerfall alle geöffneten Dateien sauber geschlossen werden. In PL/1 und Cobol half mir da ein beherztes goto :D

Danke für Eure Hilfe und Tipps.

Bis dann ...
MfG Günter
BlackJack

@niederrheiner: ``with`` oder ``try``/``finally``. Oder das ``goto``-Modul. ;-)
niederrheiner
User
Beiträge: 30
Registriert: Montag 7. Januar 2013, 11:52

Hallo an Alle,
@BlackJack: danke für Deine Antwort.Ich habe mir die Hinweise von Dir angesehen und bin der Ansicht, das diese Möglichkeiten nicht für das Problem geeignet sind. (Das goto-Modul habe ich mir nicht angesehen :) )

Ich habe mir folgendes ausgedacht und möchte Deine (eure) Meinung darüber hören: ist das für das geschilderte Problem praktikabel, und wenn nicht, was spricht dagegen.

Code: Alles auswählen

#
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
### Importbereich
import os
import sys
import logging
fd = []
## InitialisierungsBereich
# logging
logging.basicConfig(level=logging.DEBUG, format= '%(funcName)s - %(asctime)s - %(levelname)s - %(message)s')
#logging.disable(logging.CRITICAL)
#--------------------------  Funktionen
def fdclose_all(f):
    i = 0
    while i > len(f):
        f[i].close()
        i += 1
    return

if __name__ == '__main__':
    logging.debug('Start ' + __file__)
 
    # Prüfung auf Übergabeparameter
 
    # Logdateiname erstellen

    # Logdatei öffnen zum Schreiben

    # Eigentliches Hauptprogramm

    print('========================   X X X X X X    =====================')
    print('========================   B E G I N    =====================')

    # Öffnen der Dateien
    try:
        fd.append(open("testdatei1.txt", "r"))
        fd_1 = fd[len(fd) - 1]                  # <---- Vergabe eines "sprechenden Namens"
        logging.debug("Index fd_1 " + str(len(fd) - 1))

    except FileNotFoundError:
        logging.critical("testdatei1.txt konnte nicht geöffnet werden" )
        sys.exit(-1)

    try:
        fd.append(open("testdatei2.txt", "r"))
        fd_2 = fd[len(fd) - 1]                  # <---- Vergabe eines "sprechenden Namens"
        logging.debug("Index fd_2 " + str(len(fd) - 1))
    except FileNotFoundError:
        logging.critical("testdatei2.txt konnte nicht geöffnet werden")
        fdclose_all(fd)
        sys.exit(-1)

    # weitere Verarbeitung

    fd_1.write("text der in die Datei geschrieben werden soll")

    # alle geöffneten Dateien schließen
    fdclose_all()

    print('========================   E N D E    =====================')
    logging.debug('Ende ' + __file__)

Danke für Eure Hilfe und Tipps.
Bis dann ...
MfG
Günter
Zuletzt geändert von Anonymous am Montag 13. März 2017, 15:02, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@niederrheiner: Dagegen spricht auf jeden Fall schon mal dass das so nicht läuft. Da sind diverse Fehler drin. Und dann globale Variablen.

Die She-Bang-Zeile muss die *erste* Zeile sein, sonst hat sie keinen Effekt.

Dann wird `fd` viel zu früh definiert. Das wird ja erst *viel* später verwendet. Ich würde es auch nicht `fd` nennen, denn erstens ist das nicht nur ein Wert und dann ist der Wert auch nicht wirklich ein ”file descriptor”, sondern ein Dateiobjekt. „File descriptoren“ sind auf einer niedrigeren Ebene angesiedelt. Die bekommt man beispielsweise von `os.open()` als Rückgabewert.

``fd[len(files) - 1]`` ist eine recht umständliche Art ``fd[-1]`` zu schreiben. Andererseits: Wenn man das Element sowieso an einen Namen binden möchte, dann sollte man es vielleicht nicht anonym in eine Liste stecken um es dann dort wieder heraus zu holen, sondern es erst an einen Namen binden und dann erst in die Liste stecken.

Letztlich verstehe ich nicht warum ``with`` oder ``try``/``finally`` nicht funktionieren sollte.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import sys


logging.basicConfig(
    level=logging.DEBUG,
    format='%(funcName)s - %(asctime)s - %(levelname)s - %(message)s'
)
#logging.disable(logging.CRITICAL)


def main():
    filename_a = 'testdatei1.txt'
    filename_b = 'testdatei2.txt'
    try:
        with open(filename_a, 'r') as file_a:
            try:
                with open(filename_b, 'r') as file_b:
 
                    # weitere Verarbeitung
                    file_a.write('text der in die Datei geschrieben werden soll')

            except FileNotFoundError:
                logging.critical('%r konnte nicht geöffnet werden', filename_b)
                return -1
    except FileNotFoundError:
        logging.critical('%r konnte nicht geöffnet werden', filename_a)
        return -1
 
    print('========================   E N D E    =====================')
    logging.debug('Ende ' + __file__)
    return 0

 
if __name__ == '__main__':
    sys.exit(main())
Hier sieht man ganz schön warum `sys.exit()` problematisch ist, selbst wenn man es so heraus zieht wie ich das getan habe: Das Programm gibt am Ende nicht die Ende-Zeile aus wenn eine Datei nicht gefunden wurde. Da könnte ein ``try``/``finally`` helfen sicherzustellen, dass diese Ausgabe auf jeden Fall gemacht wird. Zudem kann man auch mit einem ``except FileNotFoundError:`` auskommen wenn man den Dateinamen von der Ausnahme abfragt.

Code: Alles auswählen

def main():
    try:
        with open('testdatei1.txt', 'r') as file_a:
            with open('testdatei2.txt', 'r') as file_b:

                # weitere Verarbeitung
                file_a.write('text der in die Datei geschrieben werden soll')

    except FileNotFoundError, error:
        logging.critical('%r konnte nicht geöffnet werden', error.filename)
        return -1
    finally:
        print('========================   E N D E    =====================')
        logging.debug('Ende ' + __file__)
    
    return 0
Edit: zum ``sys.exit(-1)`` vielleicht noch folgender Denkanstoss:
[codebox=text file=Unbenannt.txt]bj@god:~$ python -c 'import sys; sys.exit(-1)'
bj@god:~$ echo $?
255[/code]
Sirius3
User
Beiträge: 18299
Registriert: Sonntag 21. Oktober 2012, 17:20

zum ``sys.exit(-1)``: "negative" Exitcodes werden üblicherweise dazu verwendet, anzuzeigen, dass das Programm durch ein Signal beendet wurde.
BlackJack

@Sirius3: Jain, das kann man aber nicht mit `sys.exit()` kommunizieren weil man damit gar keine negativen Zahlen zurückgeben kann wie mein Beispiel zeigt. Zumindest unter Linux/POSIX nicht, denn die C-`exit()`-Funktion lässt nur die unteren 8 Bit durch und darum wird eine -1 zu einer positiven 255 die der Aufrufer abfragen kann. Der bekommt nur etwas negatives wenn der Prozess tatsächlich nicht normal mit `exit()` beendet wurde. Wobei auch das Implementierungsabhängig sein kann, denn um auf Signale als Ursache zu testen gibt es Makros, beziehungsweise in Python Funktionen im `os`-Modul. Es kann also durchaus sein, das auch `int`-Werte grösser als 255 für so etwas verwendet werden. Sauber müsste die Auswertung von einem Rückgabecode von einem Prozess ja so aussehen:

Code: Alles auswählen

if os.WIFEXITED(status):
    return_code = os.WEXITSTATUS(status)
    # ...
else:
    print('Process did not exit normally.')
Antworten