Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi směřující k druhému vydání.
O čem si budeme povídat?
Při tradičním přístupu může programátor po provedení nějaké akce — dejme tomu po volání funkce — otestovat korektnost výsledku. Když se například pokusíme otevřít soubor, který neexistuje, může se vracet hodnota NULL. K ošetření takových situací se používají dva obecné přístupy:
Odpovědnost za kontrolu (zda nastala chyba) a provedení příslušných akcí leží v obou případech na programátorovi.
V jazyce BASIC to může vypadat takto:
OPEN "A:\DATA.TXT" FOR INPUT AS #1
IF ERR = 53 THEN
CALL ChybaSouborNenalezen
ELSE
REM Zde pokračujeme v práci se souborem.
END IF
Při tvorbě kvalitních programů to může vést k situaci, kdy více než polovina kódu připadá na testování úspěšnosti jednotlivých akcí. Je to poněkud nešikovné a snižuje to čitelnost kódu (ale v současnosti je takto psána většina programů). Pokud se chceme vyhnout některým hloupým chybám, musíme tento postup důsledně dodržovat.
V modernějších programátorských prostředích se vyvinul alternativní způsob práce s chybami. Je znám pod pojmem ošetření výjimek a je založen na tom, že funkce vrhají (throw) nebo, jinými slovy, vyvolávají (raise) výjimky. Systém si pak zajistí vyskočení z aktuálního bloku kódu na nejbližší blok pro ošetření výjimky. V systému se nachází také blok kódu, který zachytí (catch) všechny neošetřené výjimky a obvykle zobrazí chybové hlášení a poté ukončí běh aplikace.
Blok kódu pro ošetření výjimek se trochu podobá bloku
if...then...else
:
try: # Zde se umístí hlavní část kódu programu. except TypVyjimky: # Zde se bude zpracovávat jmenovaná výjimka. except JinyTypVyjimky: # Zde se ošetřuje jiná výjimka. else: # Zde umístíme úklidový kód, který se provede # v případě, že nenastane žádná výjimka.
Existuje ještě jeden typ bloku, který souvisí s výjimkami. Umožňuje
zapsat kód pro úklid prováděný i poté, co nastala chyba. Nazývá se
try...finally
(try [tray] = zkus, pokus se vykonat; finally [fajnly] = nakonec) a
typicky se používá pro zavírání souborů, vyprazdňování vyrovnávacích pamětí
(buffer) na disk a podobně. Blok finally
je proveden vždy jako
poslední nezávisle na tom, co se stane v sekci try
. Pokud nenastane výjimka, prostě
se provede. Pokud nastane výjimka, zapamatuje se, kód bloku
finally
se provede a zapamatovaná výjimka se znovu
vyvolá.
try: # Kód, související s účelem programu. finally: # V této části provádíme úklidové akce nezávisle # na tom, zda v bloku try nastala chyba či nikoliv.
V Tcl se používá podobný mechanismus, který používá klíčové slovo
catch
([keč]
= chytit, zachytit):
set chybovyKod [catch {
unset x
} zprava ]
if {$chybovyKod != 0} {
# Tady ošetříme chybu.
}
V uvedeném případě proměnná x
neexistuje, takže nemůžeme
zrušit její hodnotu. Tcl vyvolá výjimku, ale catch
zajistí, že
program nezhavaruje. Místo toho umístí chybovou zprávu do proměnné
zprava
a vrátí nenulovou hodnotu (kterou může programátor
určit). Proto můžeme později otestovat návratovou hodnotu části
catch
, která byla uložena do proměnné chybovyKod
.
Pokud je nenulová, došlo k chybě a podrobnosti se můžeme dozvědět z proměnné
zprava
.
BASIC výjimky v pravém slova smyslu nepodporuje, ale nabízí konstrukci, která nám pomůže dodržet čistotu (přehlednost) kódu:
100 OPEN "A:\Temp.dat" FOR INPUT AS #1
110 ON ERROR GOTO 10010
120 REM Tady umístíme kód programu...
130 ...
10000 REM Vstupní body pro ošetření jednotlivých chyb:
10010 IF ERR = 54 THEN....
Povšimněte si použití čísel řádků. Ta se ve starších jazycích používala běžně. První verze jazyka BASIC nebyly výjimkou. Stejného efektu můžete v současné době dosáhnout při využití návěští (label):
ON ERROR GOTO Osetreni
REM Nyní vytvoříme chybu dělení nulou
x = 5/0
Osetreni:
IF ERR = 23 THEN
PRINT "Nelze dělit nulou!"
x = 0
RESUME NEXT
END IF
Povšimněte si příkazu RESUME NEXT
, který způsobí návrat
do místa programu těsně za příkaz, ve kterém došlo k chybě. Od tohoto místa
provádění programu pokračuje.
Jakým způsobem můžeme generovat výjimky — dejme tomu uvnitř modulu
—, které má zachytit někdo jiný? V jazyce Python je pro tento případ
vyhrazeno klíčové slovo raise
:
delenec = 42 delitel = input("Jakou hodnotou chcete dělit číslo 42?") if delitel == 0: raise "nulový dělitel"
Tento kód vygeneruje výjimku (v podobě objektu typu string), která může
být zachycena v bloku try/except
.
V Tcl může být u příkazu return
uveden nepovinný příznak
-code
, který bude zachycen libovolnou z obklopujících funkcí
catch
:
proc spam {val} { set x $val return -code 3 [expr $x] } set err [catch { set foo [spam 7] } msg]
V proměnné err
by se měla objevit hodnota 3 a v proměnné
msg
hodnota 7. Je to další případ toho, kdy je syntaxe jazyka
Tcl méně intuitivní, než by mohla být.
V jazyce BASIC můžeme proměnnou ERR nastavit příkazem
ERROR
:
ON ERROR GOTO CHYBY INPUT "Zadej kód chyby"; E ERROR E CHYBY: IF ERR = 142 THEN PRINT "Nastala chyba číslo 142" STOP ELSE PRINT "Neznámá chyba" STOP END IF
Zapamatujte si
if
.except
.raise
.Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.
$Id: cztuterrors.html,v 1.5 2004/08/31 11:55:13 prikryl Exp $