任何编程语言中都需要处理错误,错误的类型可分为语法错误和运行错误。
错误类型 | 描述 |
---|---|
语法错误 | 由于对程序的组件使用不当引起的 |
运行错误 | 程序可以正常执行但会输出报错信息 |
Lua 可使用assert
和error
函数来处理错误
错误 error
error(message [, level])
error
函数会终止正在执行的函数,并返回消息内容作为错误信息,通常会附加错误位置信息到消息头部。
参数 | 必填 | 描述 |
---|---|---|
message | 是 | 错误消息内容 |
level | 否 | 错误位置 |
错误位置 | 描述 |
---|---|
0 | 不添加错误位置信息 |
1 | 默认为调用错误位置,形式为【文件+行号】。 |
2 | 指出调用error的函数 |
注意事项
- Lua所遇到的任何未预期条件都回引发一个
error
- 只要发生了
error
,Lua就会结束当前程序块并返回应用程序。 - 显式地引发一个
error
,可调用error
函数并传入一个错误消息的参数。
示例
function fn(str)
if type(str)~="string" then
-- 告知error函数错误是发生在调用层级的第2层
error("string expected", 2)
end
end
示例
print "enter a number:"
local n = io.read("*number")
if not n then
error("invalid input")
end
> lua
> dofile("test.lua")
enter a number:
a
test.lua:4: invalid input
stack traceback:
[C]: in function 'error'
test.lua:4: in main chunk
[C]: in function 'dofile'
stdin:1: in main chunk
[C]: in ?
> nil
断言 assert
由于像if not <condition> then error end
这样的组合非常通用,所以Lua提供了内建函数assert
完成此类工作。
assert(v [, message])
- v 检查是否有错误,当为false或nil时抛出错误。
- message 可选 错误信息,当检查出有错误是抛出的信息,默认值为assertion failed!
示例:
print "enter a number:"
local n = assert(io.read("*number"), "invalid input")
> lua
> dofile("test.lua")
enter a number:
a
test.lua:2: invalid input
stack traceback:
[C]: in function 'assert'
test.lua:2: in main chunk
[C]: in function 'dofile'
stdin:1: in main chunk
[C]: in ?
> nil
编写代码时,我们总会做一些假设,断言就是用于在代码中捕捉这些假设,可将断言看做是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中某个特定点,该表达式值为真。可以在任何时候启用和禁用断言验证。因此可在测试时启动断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题是可重新启用断言。断言只有在debug
模式下才有效。
示例:
print("enter a number:")
-- 判断输入
local number = assert(io.read("*number"), "number input")
-- 判断数值
number = assert(tonumber(number), "invalid input:"..number.." is not a number")
当函数遭遇未预期的状况(异常)时,可采取两种行为:
- 返回错误代码,通常为
nil
- 引发一个错误,即调用
error
。
两种方式并没有固定法则,指导原则是易于避免的异常因引发一个错误,否则返回错误代码。
示例
print "enter a number:"
local input = io.read("*number")
local result = math.sin(input)
-- 检测异常,检测是否为数字
if not tonumber(result) then
error("invalid input number")
end
print(result)
示例
local file_handler, error_message
-- 重复执行
repeat
print "enter a file name:"
-- 判断是否输入
local input = io.read()
if not input then
return
end
-- 若文件无法打开则会返回nil与错误消息
file_handler, error_message = io.open(input, "r")
if not file_handler then
print(error_message)
end
until file_handler
$ lua
> dofile("test.lua")
enter a file name:
test.txt
test.txt: No such file or directory
若要安全地运行程序可使用assert
来检测操作
local file_handler, error_message
-- 重复执行
repeat
print "enter a file name:"
-- 判断是否输入
local input = io.read()
if not input then
return
end
-- 若要安全地运行程序可使用assert来检测操作
assert(io.open(input, "r"))
until file_handler
错误处理
对于大多数应用程序而言,开发人员无需再Lua中作任何错误处理,应用程序本身会负责此类问题。所有的Lua活动都是由应用程序的一次调用而开始的,这类调用通常要求Lua执行过一个程序块,若执行中发生错误则返回一个错误代码,这样应用程序就能采取适当的行动来处理错误。
在Lua解释器程序中发生错误时,主循环会打印错误消息,然后继续显示提示符,并等待后续命令。
$ lua
> dofile("test.lua")
enter a file name:
test.txt
test.txt: No such file or directory
若需要在Lua中处理错误,必须使用函数pcall(protected call)
来包装需要执行的代码。pcall
接收一个函数和要传递给后者的参数并执行。
pcall(func)
pcall
以一种“保护模式”来调用第一个参数,可同时捕获函数执行中的任何错误和异常。若被执行函数一切正常,pcall
返回true
以及被执行函数函数的返回值,否则返回nil
和错误信息。也就是说成功仅仅有一个返回值,而失败则有两个返回值。
示例
pcall(function()
require("channel")
end)
通常在错误发生时,希望获得更多的调试信息,而不只是发生错误的位置。但pcall
返回时已经销毁了调用栈的部分内容,Lua提供了xpcall
来实现这个功能。
pcall
以一种“保护模式”来调用第一个参数,因此可捕获函数执行中的任何错误。通常在错误发生时,希望获得更多的调试信息,而不只是发生错误的位置。但pcall
返回时已经销毁了调用栈的部分内容。
错误追溯
通常在错误发生时,我们希望得到更多的调试信息,而不仅仅是发生错误的位置。至少能够追溯到发生错误时的函数调用情况,显示一个完整的函数调用栈。
当pcall
返回其错误消息时,已经摧毁了函数调用栈的部分内容,因此,如果希望得到一个有意义的函数调用栈,那么久必须在pcall
返回前获取该信息。因此,如果希望得到一个有意义的函数调用栈,就必须在pcall
返回前获取该信息。为此,Lua提供了函数xpcall
,该函数除了接收一个需要被调用的函数外,还接受一个错误处理函数
xpcall(func, handler)
Lua提供了xpcall
函数接收的第二个参数是一个错误处理的函数,当错误发生时,Lua会在调用栈展看unwind
前调用错误处理函数,于是就可以在这个函数中使用debug
库来获取关于错误的额外信息。
debug
库提供了两个通用的错误处理函数:
-
debug.debug
提供一个Lua提示符让用户来处理错误的原因 -
debug.traceback
根据调用栈来构建一个扩展的错误消息
示例
xpcall(function(i)
print(i)
end,function()
print(debug.traceback())
end,100)
```。