Was werden wir behandeln? |
---|
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:
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.
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.
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.
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.
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 |
---|
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