鍍金池/ 教程/ Java/ 錯(cuò)誤處理
數(shù)據(jù)庫訪問
循環(huán)
數(shù)組
錯(cuò)誤處理
面向?qū)ο?/span>
調(diào)試
游戲開發(fā)
文件 I/O
變量
迭代器
Web 編程
模塊
函數(shù)
元表
協(xié)程
垃圾回收機(jī)制
標(biāo)準(zhǔn)庫
決策
數(shù)據(jù)類型
運(yùn)行環(huán)境
操作符
字符串
基本語法
概述

錯(cuò)誤處理

為什么需要錯(cuò)誤處理機(jī)制

在真實(shí)的系統(tǒng)中程序往往非常復(fù)雜,它們經(jīng)常涉及到文件操作、數(shù)據(jù)庫事務(wù)操作或網(wǎng)絡(luò)服務(wù)調(diào)用等,這個(gè)時(shí)候錯(cuò)誤處理就顯得非常重要。不關(guān)注錯(cuò)誤處理可能在處理諸如涉密或金融交易這些業(yè)務(wù)時(shí)造成重大的損失。
無論什么時(shí)候,程序開發(fā)都要求小心地做好錯(cuò)誤處理工作。在 Lua 中錯(cuò)誤可以被分為兩類:

  • 語法錯(cuò)誤
  • 運(yùn)行時(shí)錯(cuò)誤

語法錯(cuò)誤  

語法錯(cuò)誤是由于不正確的使用各種程序語法造成的,比如錯(cuò)誤的使用操作符或表達(dá)式。下面即是一個(gè)語法錯(cuò)誤的例子:

a == 2

正如你知道的那樣,單個(gè)等號與雙等號是完全不一樣的。二者之間隨意的替換就導(dǎo)致語法錯(cuò)誤。一個(gè)等號表示的是賦值,而雙等號表示比較。類似地,下面這一小段代碼中也存在語法錯(cuò)誤:

for a= 1,10
   print(a)
end

執(zhí)行上面的這段程序,我們會得到如下的輸出結(jié)果:

lua: test2.lua:2: 'do' expected near 'print'

語法錯(cuò)誤相比于運(yùn)行時(shí)錯(cuò)誤更容易處理,因?yàn)?Lua 解釋器可以更精確的定位到語法錯(cuò)誤的位置。由上面的錯(cuò)誤,我們可以容易就知道,在 print 語句前添加 do 語句就可以了,這是 Lua 語法結(jié)構(gòu)所要求的。

運(yùn)行時(shí)錯(cuò)誤

對于運(yùn)行時(shí)錯(cuò)誤,雖然程序也能成功運(yùn)行,但是程序運(yùn)行過程中可能因?yàn)殄e(cuò)誤的輸入或者錯(cuò)誤的使用函數(shù)而導(dǎo)致運(yùn)行過程中產(chǎn)生錯(cuò)誤。下面的例子顯示了運(yùn)行時(shí)錯(cuò)誤如何產(chǎn)生的:

function add(a,b)
   return a+b
end

add(10)

當(dāng)我們嘗試生成(build)上面的程序,程序可以正常的生成和運(yùn)行。但是一旦運(yùn)行后,立馬出現(xiàn)下面的運(yùn)行時(shí)錯(cuò)誤。

lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value)
stack traceback:
    test2.lua:2: in function 'add'
    test2.lua:5: in main chunk
    [C]: ?

這個(gè)運(yùn)行時(shí)錯(cuò)誤是由于沒有正確的為 add 函數(shù)傳入?yún)?shù)導(dǎo)致的,由于沒有為 b 傳入值,所有 b 的值為 nil 從而導(dǎo)致在進(jìn)行加法運(yùn)算時(shí)出錯(cuò)。

Assert and Error 函數(shù)

我們經(jīng)常用到 assert 和 error 兩個(gè)函數(shù)處理錯(cuò)誤。下面是一個(gè)簡單的例子。

local function add(a,b)
   assert(type(a) == "number", "a is not a number")
   assert(type(b) == "number", "b is not a number")
   return a+b
end
add(10)

執(zhí)行上面的程序,我們會得到如下的輸出結(jié)果:

lua: test2.lua:3: b is not a number
stack traceback:
    [C]: in function 'assert'
    test2.lua:3: in function 'add'
    test2.lua:6: in main chunk
    [C]: ?

error(message [,level]) 函數(shù)會結(jié)束調(diào)用自己的函數(shù),并將 message 作為錯(cuò)誤信息返回調(diào)用者(譯注:保護(hù)模式下才會返回調(diào)用者,一般情況會結(jié)束程序運(yùn)行并在控制終端輸出錯(cuò)誤信息)。error 函數(shù)本身從不返回。一般地,error 函數(shù)會在消息前附上錯(cuò)誤位置信息。級別(level) 參數(shù)指定錯(cuò)誤發(fā)生的位置。若其值為 1(默認(rèn)值),返回的錯(cuò)誤的位置是 error 函數(shù)被調(diào)用的位置。若為 2, 返回的錯(cuò)誤位置為調(diào)用 error 函數(shù)的函數(shù)被調(diào)用的位置,依次類推。將 level 參數(shù)的值設(shè)為 0 就不再需要在消息前增加額外的位置信息了。

pcall 與 xpcall  

在 Lua 中,為了避免使用拋出錯(cuò)誤和處理錯(cuò)誤,我們需要用到 pcall 和 xpcall 函數(shù)來處理異常。
使用 pcall(f,arg1,...) 函數(shù)可以使用保護(hù)模式調(diào)用一個(gè)函數(shù)。如果函數(shù) f 中發(fā)生了錯(cuò)誤, 它并不會拋出一個(gè)錯(cuò)誤,而是返回錯(cuò)誤的狀態(tài)。使用的 pcall 函數(shù)的方法如下所示:

function myfunction ()
   n = n/nil
end

if pcall(myfunction) then
   print("Success")
else
    print("Failure")
end

執(zhí)行上面的程序,我們可以得到如下的輸出結(jié)果:

Failure

xpcall(f,err) 函數(shù)調(diào)用函數(shù) f 同時(shí)為其設(shè)置了錯(cuò)誤處理方法 err,并返回調(diào)用函數(shù)的狀態(tài)。任何發(fā)生在函數(shù) f 中的錯(cuò)誤都不會傳播,而是由 xpcall 函數(shù)捕獲錯(cuò)誤并調(diào)用錯(cuò)誤處理函數(shù) err,傳入的參數(shù)即是錯(cuò)誤對象本身。xpcall 的使用示例如下:

function myfunction ()
   n = n/nil
end

function myerrorhandler( err )
   print( "ERROR:", err )
end

status = xpcall( myfunction, myerrorhandler )
print( status)

執(zhí)行上面的程序,我們可以得到如下的輸出結(jié)果:

ERROR:  test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false

作為程序開發(fā)人員,在程序中正確合理地處理錯(cuò)誤是非常重要的。正確地處理錯(cuò)誤可以保證發(fā)生意外情況不會影響到程序用戶的使用。