Fehlerbehandlung

Was werden wir behandeln?
  • Zwei Arten, um mit Fehlern umzugehen
  • Auslösen von Fehlern in unserem Code zum Abfangen durch andere
  • Der traditionelle Weg

    Wenn Programmierer üblicherweise etwas tun, sagen wir einmal eine Funktion aufrufen, kann das Ergebnis auf Gültigkeit überprüft werden. Zum Beispiel wenn du versuchst, eine Datei zu öffnen, die nicht existiert, so ist der zurückgegebene Wert ein Nullwert. Es gibt zwei allgemeine Strategien, um mit dieser Art von Situationen umzugehen:

    1. Einschließen des Fehlercodes in das Ergebnis der Funktion oder
    2. Setzen einer globalen Variable auf einen Fehlerzustand.

    In den jeweiligen Fällen liegt es am Programmierer zu überprüfen, ob ein Fehler aufgetreten ist und eine zugehörige Aktion auszuführen.

    In BASIC sieht das so aus:

    OPEN "A:\DATA.TXT" FOR INPUT AS #1
    IF ERR = 53 THEN 
       CALL DateiNichtGefundenFehler
    ELSE
       REM HIER ZUR FEHLERBEHANDLUNG FORTSETZEN
    END IF
    

    Dies kann bei der Produktion von Programmen hoher Qualität als Ergebnis haben, dass über die Hälfte des Codes dazu verwendet wird, um zu Testen, ob jede Aktion zum Erfolg führt. Dies ist beschwerlich und macht den Code schwierig zu lesen (aber in der Praxis arbeitet so die Mehrzahl der heutigen Programme). Eine konsistente Behandlung ist essentiell, wenn törichte Fehler vermieden werden sollen.

    Der Ausnahme-Weg

    In moderneren Programmierumgebungen wurde ein alternativer Weg im Umgang mit Fehlern entwickelt. Die ist bekannt als Ausnahmebehandlung (exception handling) und wirkt durch Funktionen, die eine Ausnahme unterdrücken oder auslösen. Das System erzwingt dann einen Sprung aus dem laufenden Codeblock zum nächsten Ausnahmebehandlungsblock. Das System ist mit einem Ersatz-Behandler ausgestattet, dass alle Ausnahmen auffängt, eine Fehlermeldung ausgibt und dann beendet.

    Der Ausnahmebehandlungsblock wird fast wie ein if...then...else-Block kodiert:

    try:
       # hier erfolgt normale Programmierlogik
    except Ausnahmetyp:
       # Ausnahmebearbeitung der benannten Ausnahme erfolgt hier
    except EinandererTyp:
       # Ausnahmebearbeitung der anderen Ausnahme erfolgt hier
    else:
       # das hier wird ausgeführt, wenn KEINE Ausnahme ausgelöst wird
    

    Es gibt eine andere Art von 'Ausnahme'-Block, der es erlaubt, nach einem Fehler fortzufahren, der try...finally-Block genannt wird und typischerweise zum Schließen von Dateien verwendet wird, zum Überführen von Puffern auf eine Diskette usw. Der finally-Block wird zum Schluss immer ausgeführt, unbeachtet, was im try-Abschnitt geschieht.

    try:
       # normale Programmlogik
    finally:
       # das hier wird ausgeführt unbeachtet des
       # Erfolgs/Misserfolgs des try-Blockes
    

    Tcl hat einen irgendwie ähnlichen Mechanismus unter der Verwendung des Schlüsselwortes catch:

    set errorcode [catch {
        unset x
        } msg ]
    if {$errorcode != 0} {
        # hier Behandlung des Fehlers
        }
    

    In diesem Falle existiert x nicht, so dass wir es nicht mit unset behandeln können. Tcl löst eine Ausnahme aus, aber das catch bewahrt das Programm vor dem Abbrechen und anstatt dessen steckt es eine Fehlermeldung in die msg-Variable hinein und gibt einen Wert ungleich Null zurück (der durch den Programmierer definiert werden kann). Du kannst dann den Rückgabewert des Fehlereinfanges in errorcode testen. Wenn er nicht Null ist, dann war ein Fehler aufgetaucht und du kannst die msg-Variable untersuchen.

    BASIC unterstützt Ausnahmen allerdings nicht, aber es hat ein Konstrukt, das den Code klar verständlich hält:

    100 OPEN "A:\Temp.dat" FOR INPUT AS #1
    110 ON ERROR GOTO 10010
    120 REM HIER STEHT DER PROGRAMMCODE...
    130 ...
    10000 REM FEHLERBEHANDLUNG:
    10010 IF ERR = 54 THEN....
    

    Beachte den Gebrauch der Zeilennummern. Dies war in anderen Programmiersprachen üblich, einschließlich dem frühen BASIC. Jetzt kannst du das Gleiche mit Labels machen:

    ON ERROR GOTO Handler
    REM Jetzt wird ein Fehler durch Nulldivision erzeugt
    x = 5/0
    Handler:
       IF ERR = 23 THEN 
          PRINT "Kann nicht durch Null dividieren"
          x = 0
          RESUME NEXT
       END IF
    

    Beachte die RESUME NEXT-Anweisung, die es uns erlaubt, genau hinter die Stelle des Fehlers zurückzukehren und mit dem Programm fortzufahren.

    Generierung von Fehlern

    Was geschieht, wenn wir Ausnahmen generieren wollen, die von anderen Leuten abgefangen werden können, sagen wir in einem Modul? In diesem Fall verenden wir das raise-Schlüsselwort in Python:

    numerator = 42
    denominator = input("Welchen Wert soll ich durch 42 dividieren?")
    if denominator == 0:
       raise "Null-Denominator"
    

    Dies löst eine Stringobjektausnahme aus, die durch einen try/except-Block abgefangen werden kann.

    Tcl's Fehlermechanismus

    In Tcl übernimmt ein optionaler-code-Flag die return-Anweisung, die durch ein eingeschlossenes catch abgefangen wird:

    proc frikadelle {val} {
            set x $val
            return -code 3 [expr $x]
            }
    set err [catch {
                   set iss [frikadelle 7]
                   } msg]
    

    err soll den Wert 3 haben und msg den Wert 7. Schon wieder ein Fall, bei dem die Syntax von Tcl weniger intuitiv ist, als sie sein sollte.

    Fehlerbehandlung in BASIC

    In BASIC kannst du die ERR-Variable mit der ERROR-Anweisung erstellen:

    ON ERROR GOTO ERRORS
    INPUT "EINGABE DES FEHLERCODE"; E
    ERROR E
    
    ERRORS:
    IF ERR = 142 THEN
        PRINT "Fehler 142 gefunden"
        STOP
    ELSE
        PRINT "Kein Fehler gefunden"
        STOP
    END IF
    
    


    Zum Behalten
  • Überprüfung von Fehlermeldungen mit einer if-Anweisung
  • Abfangen von Ausnahmen mit einer Ausnahmeklausel
  • Generierung von Ausnahmen mit Hilfe des raise-Schlüsselwortes
  • Fehler können ein einfacher String sein
  • zurück  weiter  Inhalt


    Im Falle von Fragen und Hinweisen sende bitte eine Nachricht auf Englisch an alan.gauld@yahoo.co.uk oder auf Deutsch an bup.schaefer@freenet.de