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 bude do subjektu dopisu automaticky vložena informace o jméně a verzi tohoto HTML dokumentu.
$Id: cztuterrors.html,v 1.2 2004/05/14 10:22:45 prikryl Exp $