2022年的 Tcl/Tk 編程入門參考 7.異常處理
學(xué)習(xí)異常處理部分時,推薦用tclsh來執(zhí)行。因為在一部分情況下,tclsh的報錯內(nèi)容比wish更詳細。
異常處理是一種機制,絕大部分編程語言都有,而且機制都差不多。在實際編程中,經(jīng)常會出現(xiàn)預(yù)料之外的情況,比如說:在批量處理文本文件時,某個文件被系統(tǒng)鎖定了;再比如下載文件,網(wǎng)斷了。這些特殊情況,會讓原本正常的代碼,崩潰退出。為了應(yīng)對各種奇葩的意外情況,編程語言普遍都準備異常處理機制,通過這個機制,即便出現(xiàn)了意外,也能讓整個程序繼續(xù)運行下去。
因為各人思路不一樣,在此我只提千萬不要干的事情:絕對不要通過異常處理機制去掩蓋錯誤!
Tcl語言通過try和catch兩個命令,可以做到在程序運行時不報錯,最簡單的就是把代碼放到catch命令里面,但是這種做法根本沒有處理異常,只是在掩蓋異常。即使是個人程序,也不要這么干。異常處理機制是用來處理異常的,不是掩蓋異常的。
實際上一個沒有異常處理的程序并不可怕,因為有問題就報錯中止運行了。最可怕的程序就是掩蓋異常的程序,我們根本不知道它運行的時候有沒有異常發(fā)生,結(jié)果不可靠,不能被信任。而且越是有經(jīng)驗的程序員寫的就越危險,他們的代碼99%的情況都按預(yù)期運行,那么掩蓋的就是那1%的意外,就這1%他自己都說不清楚什么時候會發(fā)生,完全沒有規(guī)律,屆時查錯能坑死人。
異常變量
每當出錯時,TCL語言會將相關(guān)信息保存在固定名稱的變量里:
errorInfo:TCL會穿透式的追蹤錯誤位置(有代碼嵌套時)追蹤過程及報錯信息會保存在該變量中
errorCode:以列表形式保存的錯誤信息,包括錯誤類型和錯誤代碼
catch
catch 命令:執(zhí)行代碼,如出錯則捕獲錯誤
catch {代碼段} ?信息變量名? ?詳細信息(字典)變量名?
例子里yy變量,以字典的形式保存了一堆的報錯信息(為便于觀看,我分了行)。字典的內(nèi)容里,errorcode 和 errorInfo 是預(yù)設(shè)變量。
code 1表示報錯了,如果正常運行,code就是0。
level條目的值在絕大多數(shù)情況下都是0,只有在代碼段以return語句返回時,才會是其它值。
errorline 記錄出錯位置的行號
errorstack 記錄的是堆棧的跟蹤信息。本例的 INNER 表示內(nèi)部錯誤,其它值請大家參考官方文檔。
error
error ?報錯消息 ??errorInfo內(nèi)容? ??errorCode內(nèi)容?
手動生成一個異常,并且可以選擇將指定內(nèi)容存放于errorInfo、errorCode變量中
throw
手動生成與Tcl解釋器內(nèi)置錯誤相同的異常。該命令與error的區(qū)別:Tcl語言解釋器(tclsh、wish)報錯信息都屬于內(nèi)置異常(有固定的類型和提示信息),而error生成的異常并不屬于內(nèi)置異常。throw可以生成與Tcl語言解釋器相同的異常,所以官方文檔里稱其為"機器可讀"的異常。
throw 錯誤類型 報錯消息
??這個命令可以搭配后面介紹的try trap命令,手動生成某種類型的異常,然后再對這種類型進行處理(然而實事求是的說,這也太繁瑣了,我寧可把代碼段拆分,也不會去干這么摧殘發(fā)根的事)
?
try
8.6版新增命令。嘗試運行代碼段,根據(jù)情況進行對應(yīng)處理,與其它語言try函數(shù)類似。
在TCL/TK安裝目錄 \lib\tcllib1.x\try 下面,有用純TCL語言寫的 try 命令向下兼容版。
try ?{代碼段} ?處理部分 ??finally {代碼段}?
雖然"處理部分"和finally {代碼段}都可以省略,但這種情況下try命令沒有任何意義,所以事實上try必須至少搭配一個后續(xù)處理代碼段。
?
異常處理 on、trap
try命令的異常處理部分有兩種形式,都跟switch命令類似,以匹配執(zhí)行情況來進行對應(yīng)的處理。官方文檔里是以trap形式來舉例的,那我就以on為主來介紹一下。
on的用法是以運行結(jié)果標志為基準,進行對應(yīng)的處理。
on ?運行結(jié)果標志 ?變量名列表 ?{處理代碼段}
運行結(jié)果一共有五種情況:正常運行、出錯、return、break、continue。這5種情況的標識,即可以用數(shù)字的0~4來表示,也可用英文單詞來表示,即:0-ok, 1-error, 2-return, 3-break, 4-continue
變量名列表:如果只給出一個變量名,這個變量就保存try命令代碼段執(zhí)行的結(jié)果。如果給出了兩個變量名,第二變量里就以字典的形式,保存一堆的運行結(jié)果的信息,跟catch命令的“詳細信息”內(nèi)容是一樣的。
?
trap 根據(jù)錯誤類型執(zhí)行代碼段。就是前面 throw 命令里提到的,Tcl各種內(nèi)置錯誤
trap ?錯誤類型 ?變量名列表 ?{處理代碼段}
?
本例中完整異常類型為 TCL ?LOOKUP ?COMMAND。其中"TCL"是一種大類型,其中包括"LOOKUP",而"COMMAND"是更具體的子類型。當設(shè)置為 trap {TCL}時,表示所有歸為"TCL"類的異常都這樣處理。
更多內(nèi)置異常類型請去官網(wǎng)搜索,如果文檔里沒有就去官方wiki翻翻
finally
finally {代碼段} :無論try結(jié)果如何,都會執(zhí)行代碼段,除非退出TCL解釋器
更多try命令介紹,可以參考wiki內(nèi)容:wiki.tcl-lang.org/page/try
return
還有一種返回異常的方式是通過return命令,更準確的說是返回狀態(tài)。catch和try命令以字典的形式保存的異常"詳細信息",也可以用于return命令。
return -code 狀態(tài)標識(0-ok, 1-error, 2-return, 3-break, 4-continue)
return -options ?$異常詳細信息 ?$提示信息
更多信息參考return的官方文檔