看看天空 发表于 2020-3-12 23:56

lua 学习之错误处理

# 错误处理



## 动态链接



1. 在 Lua 中,动态链接被视为所有其他机制的母机制
2. 因此利用它就可以动态地加载任何其他不在 Lua 中的机制



1. `package.loadlib` 所需两个参数
   1. 库的完整路径
   2. 正确的函数名称
2. `loadlib` 函数加载指定的库,并将其链接入 Lua
3. 它没有调用库中的任何函数
4. 而是将一个 C 编写的函数作为 Lua 函数返回
5. 如果加载库或查找初始化函数时发生错误,会返回 `nil` 及一条错误信息



```lua
local path = "c:/lua/5.1/socket.so"
local f = package.loadlib(path, "luaopen_socket")
```



1. 通常使用 `require` 来加载 C 程序库,这个函数会搜索指定的库
2. 然后用 `loadlib` 加载库,并返回初始化函数
3. 这个初始化函数应将库中提供的函数注册到 Lua 中,就类似 Lua 代码块中定义了其他函数





## 错误



1. Lua 是一种扩展语言,通常嵌入在应用程序中
2. 如果发生错误时如果直接崩溃或退出,那么我们就无法捕获到错误出现在哪里
3. 因此 Lua 只要发生一个错误,就应该结束当前程序块并返回应用程序



1. Lua 中任何未预期条件都会引发一个错误,如:
   1. 将两个非数字的值相加
   2. 对一个不是函数的值进行调用操作
   3. 索引一个不是 Table 的值
2. 可以通过调用 `error` 函数,显式地引发发一个错误
   1. 需要传入一个错误消息的函数



```lua
do
    print("enter a number:")
    n = io.read("*number")
    if not n then
      error("invalid input")
    end
end

-- 与上述代码等效
do
        print("enter a number:")
        n = assert(io.read("*number"), "invalid input")
end
```



1. `assert` 如果第一个参数为 `true` 则返回该参数
2. 如果第一个参数为 `false` 或 `nil` 就会引发一个错误
3. 第二个参数为一个可选的信息字符串
4. 在调用 `assert` 时会对其参数求值
5. 下述代码中,即使 `n` 是数字类型, Lua 也会进行字符串连接



```lua
n = io.read()
assert(tonumber(n), "invalid input:" .. n .. " is not a number")
```



1. 当一个函数遭遇了一种未预期的情况即「异常」,可以采取两种基本行为
   1. 返回错误代码 (通常是 `nil`)
   2. 引发一个错误(调用 `error`)

2. `sin` 传入 `table` 作为参数



```lua
-- 返回一个错误代码,检查 sin 函数返回值
local res = math.sin(x)
if not res then
        <错误处理代码>
end

-- 调用 sin 之前,检查参数
if not tonumber(x) then
           <错误处理代码>
end
```



1. 通常既不检查参数也不会检查 `sin` 的返回值
2. 可以停止计算,然后给出错误消息



1. `io.open` 文件不存在的情况或拒绝访问时的异常行为
2. 一个文件存在与否可以用是否能够打开来验证
3. 当 `io.open` 无法打开一个文件时,应返回 `nil` ,并附加一条错误消息



```lua
do
    local file, msg
    repeat
      print("enter a file name:")
      local name = io.read()
      if not name then
            return
      end
      -- io.open 第一个参数为文件路径,第二个参数为打开模式,r 为字符模式
      -- io.open 成功则返回文件句柄,无法打开会返回 nil 和错误消息
      file, msg = io.open(name, "r")
      if not file then
            print(msg)
      end
        until file
end

-- 等效于上述代码.错误消息时 io.open 的第二个返回值,并成为了 assert 的第二个参数
do
    local file, msg
    repeat
      print("enter a file name:")
      local name = io.read()
      if not name then
            return
      end
       file = assert(io.open(name, "r"))
        until file
end
```



## 错误处理与异常



1. 大多数情况无需在,Lua 中作任何错误处理,由调用 Lua 的应用程序来负责
2. 因为所有的 Lua 活动都是由应用程序的一次调用而开始的
   1. 通常是要求 Lua 执行一个程序块
   2. 如果发生错误,此调用就会返回错误代码,并由应用程序处理
3. 在解释器程序中发生错误时,主循环会打印错误消息,然后继续显示提示符,并等待执行后续命令



1. 在 Lua 中处理错误,**必须使用** `pcall` 来包裹要执行的代码, p-->意为 **protect** 保护的意思
2. `pcall` 可以捕获函数执行时引发的任何错误
   1. 如果没有错误,就会返回 `true` 以及函数调用的返回值
   2. 如果有错误,就会返回 `false` 以及错误消息



```lua
-- 执行一段 Lua 代码,捕获所有执行中发生的错误,需先将这段代码封装到一个函数中
function foo()
    <code block>
    if 未预期的条件 then
      error()
    end
    <code block>
    print(a) -- 潜在的错误,a 可能不是一个 table
    <code block>
end

if pcall(foo) then
    -- 执行 foo 时没有发生错误
    <常规代码>
else
    -- foo 引发了一个错误,进行错误处理
    <错误处理代码>
end
```



1. 调用 `pcall` 时可以传入一个匿名函数

2. 「错误消息」可以是任何值,并将其传递给 `error` 函数,这些值也就成为 `pcall` 的返回值

   

```lua
if pcall (function ()
        -- <受保护的代码>      
    return 20 + 10 -- 用于测试的代码 "a" + 10
end) then
    -- <常规代码>
    print("ok")
else
    -- <错误处理代码>
    print("error")
end

do
        local status, err = pcall(function ()
      error({code = 121})
        end)
    print(status, err.code)
end
```



1. 在 Lua 中一个完整异常处理流程通常是:
   1. 使用 `error`来抛出异常
   2. 使用 `pcall` 来捕获异常
   3. 错误消息用来标识错误类型或内容



## 错误消息与追溯



1. 错误消息通常是一个描述出错内容的字符串
2. Lua 遇到一个内部错误,如索引一个非 `table` 的值,就会产生错误消息
3. 其他情况下是错误消息是传递给 `error` 函数的值
4. 只要错误消息是一个字符串,Lua 就会附加一些**错误发生位置的信息**





```lua
do
    local status, err = pcall(function () a ="a"+1 end)
    print(err)
end

do
    local status, err = pcall(function ()
            error("my         error")
    end)
    print(err)
end
-- 位置信息包含文件名 stdin 及行号 3
-- stdin:3: my error
```



1. `error` 函数的第二个参数 `level` ,用于指出应有调用层级中的那个(层)函数来报告当前的错误,即谁为错误负责



```lua
-- 在一个函数中,一开始就检查传入参数是否正确
do
    function foo(str)
      if type(str) ~= "string" then
            -- 不加第二个参数,则认为是读函数时出错并报告错误, stdin:3: string expected
            -- error("string expected")
            -- 加上第二个参数,则认为是在调用层出错并报告错误, stdin:9: string expected
            error("string expected", 2)
      end
      print(str)
    end

    foo({x = 1})
end

```



1. `pcall` 函数返回错误消息时,它已经销毁了调用栈的部分内容

2. 如果要获取完整的追溯到发生错误时的函数调用情况,而不是仅仅获取到错误发生的位置,需要用到 `xpcal`l 函数

3. `xpcall` 函数接受两个参数

   1. 需要被调用的函数
   2. 以及一个错误处理函数

4. 发生错误时,Lua 会在调用**栈展开**前调用错误处理函数,就可以用 `debug` 库来获取错误的额外信息

   

### debug 库的两个通用处理函数



1. `debug.debug` ,提供一个 Lua 提示符,让用户检查错误的原因
2. `debug.traceback` ,根据调用栈来构建一个扩展的错误消息
3. **解释器程序**使用 `debug.traceback` 来构建其错误消息
4. 任何时候调用 `debug.traceback` 都可以获取**当前执行的调用栈**



```lua
do
    local t = {2, 4, 6, 8 ,10}
    for i,v in ipairs(t) do
               print(i, v)
      print(debug.traceback())
    end
end
```

dongtian123sss 发表于 2020-3-13 00:14

支持一下
页: [1]
查看完整版本: lua 学习之错误处理