美文网首页
lua 学习之错误处理

lua 学习之错误处理

作者: 跃千山 | 来源:发表于2020-03-13 11:54 被阅读0次

    错误处理

    动态链接

    1. 在 Lua 中,动态链接被视为所有其他机制的母机制
    2. 因此利用它就可以动态地加载任何其他不在 Lua 中的机制
    1. package.loadlib 所需两个参数
      1. 库的完整路径
      2. 正确的函数名称
    2. loadlib 函数加载指定的库,并将其链接入 Lua
    3. 它没有调用库中的任何函数
    4. 而是将一个 C 编写的函数作为 Lua 函数返回
    5. 如果加载库或查找初始化函数时发生错误,会返回 nil 及一条错误信息
    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. 需要传入一个错误消息的函数
    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. 如果第一个参数为 falsenil 就会引发一个错误
    3. 第二个参数为一个可选的信息字符串
    4. 在调用 assert 时会对其参数求值
    5. 下述代码中,即使 n 是数字类型, Lua 也会进行字符串连接
    n = io.read()
    assert(tonumber(n), "invalid input:" .. n .. " is not a number")
    
    1. 当一个函数遭遇了一种未预期的情况即「异常」,可以采取两种基本行为

      1. 返回错误代码 (通常是 nil
      2. 引发一个错误(调用 error
    2. sin 传入 table 作为参数

    -- 返回一个错误代码,检查 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 ,并附加一条错误消息
    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 代码,捕获所有执行中发生的错误,需先将这段代码封装到一个函数中
    function foo()
        <code block>
        if 未预期的条件 then 
            error()
        end
        <code block>
        print(a[i]) -- 潜在的错误,a 可能不是一个 table
        <code block>
    end
    
    if pcall(foo) then
        -- 执行 foo 时没有发生错误
        <常规代码>
    else
        -- foo 引发了一个错误,进行错误处理
        <错误处理代码>
    end
    
    1. 调用 pcall 时可以传入一个匿名函数

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

    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 就会附加一些错误发生位置的信息
    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 ,用于指出应有调用层级中的那个(层)函数来报告当前的错误,即谁为错误负责
    -- 在一个函数中,一开始就检查传入参数是否正确
    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. 如果要获取完整的追溯到发生错误时的函数调用情况,而不是仅仅获取到错误发生的位置,需要用到 xpcall 函数

    3. xpcall 函数接受两个参数

      1. 需要被调用的函数
      2. 以及一个错误处理函数
    4. 发生错误时,Lua 会在调用栈展开前调用错误处理函数,就可以用 debug 库来获取错误的额外信息

    debug 库的两个通用处理函数

    1. debug.debug ,提供一个 Lua 提示符,让用户检查错误的原因
    2. debug.traceback ,根据调用栈来构建一个扩展的错误消息
    3. 解释器程序使用 debug.traceback 来构建其错误消息
    4. 任何时候调用 debug.traceback 都可以获取当前执行的调用栈
    do
        local t = {2, 4, 6, 8 ,10}
        for i,v in ipairs(t) do
            print(i, v)
            print(debug.traceback())
        end
    end
    

    本篇文章由一文多发平台ArtiPub自动发布

    相关文章

      网友评论

          本文标题:lua 学习之错误处理

          本文链接:https://www.haomeiwen.com/subject/uvmqshtx.html