Skip to content

📚 Lua基础库 - 新手必看的入门指南 ​

六个 Lua 标准库:base、string、table、math、io、os 各管什么

📖 什么是Lua基础库? ​

Lua基础库就像是编程的"工具箱",里面有最常用的基本工具。这些功能不需要额外加载,打开软件就能直接使用,就像手机里自带的计算器、时钟一样方便。

能做什么:

  • 💬 显示信息和调试程序
  • 🔢 处理数字和文字
  • ✅ 检查数据类型
  • 🛠️ 处理错误和异常

🚀 3分钟快速上手 ​

第1步:显示信息 ​

lua
-- 在屏幕上显示文字(最常用的功能)
print("Hello World")
print("程序开始运行")

-- 显示多个内容
print("温度:", 25, "度")
print("设备状态:", "正常")

第2步:处理数据 ​

lua
-- 定义变量(存储数据的盒子)
local 温度 = 25.5
local 设备名称 = "温控器"
local 运行中 = true

-- 显示变量内容
print("当前温度:", 温度)
print("设备:", 设备名称)
print("是否运行:", 运行中)

第3步:数据转换 ​

lua
-- 把文字转换成数字
local 文字数字 = "123"
local 真正数字 = tonumber(文字数字)
print("转换结果:", 真正数字)  -- 显示:123

-- 把数字转换成文字
local 数字 = 456
local 文字 = tostring(数字)
print("转换结果:", 文字)      -- 显示:"456"

-- 安全调用函数
local success, result = pcall(function()
    return 10 / 0  -- 可能出错的代码
end)

if success then
    print("结果:", result)
else
    print("错误:", result)
end

📚 API参考 ​

📋 函数列表 ​

函数名功能描述示例
📋 type(value)获取数据类型type(123) → "number"
🔢 tonumber(str)转换为数字tonumber("123") → 123
📝 tostring(value)转换为字符串tostring(123) → "123"
💬 print(...)输出内容print("Hello", 123)
🛡️ pcall(func, ...)安全调用函数pcall(math.sqrt, -1)
⚠️ assert(condition, msg)断言检查assert(x > 0, "x必须大于0")

📝 实用示例 ​

数据验证 ​

lua
function validate_number(value, name)
    assert(value ~= nil, name .. "不能为空")
    local num = tonumber(value)
    assert(num ~= nil, name .. "必须是数字")
    return num
end

-- 使用
local age = validate_number("25", "年龄")
print("年龄:", age)

安全除法 ​

lua
function safe_divide(a, b)
    if type(a) ~= "number" then a = tonumber(a) end
    if type(b) ~= "number" then b = tonumber(b) end
    
    if not a or not b then
        return nil, "参数必须是数字"
    end
    
    if b == 0 then
        return nil, "除数不能为0"
    end
    
    return a / b
end

-- 使用
local result, err = safe_divide(10, 2)
if result then
    print("结果:", result)
else
    print("错误:", err)
end

⚠️ 注意事项 ​

  1. 类型转换:使用 tonumber() 和 tostring() 进行安全转换
  2. 错误处理:重要操作使用 pcall() 进行保护
  3. 断言检查:使用 assert() 验证输入参数
  4. 调试输出:使用 print() 输出调试信息

例程 ​

基础库(type/tonumber/tostring/print/pcall/assert)是 Lua 自带的,无需加载模块。下面例程都是可直接下载运行的完整脚本:顶层执行完即结束,不阻塞固件主循环。工业现场最常见的用途是「把外部进来的字符串安全变成数字」和「兜住会出错的外设调用」。

例程 1:把云/串口下发的字符串安全转成数字 ​

适用:全部机型 / 软PLC模拟器 / 上位机 | 依赖模块:base | 难度:★☆☆

场景:云端或串口下发的是 "25.6" 这种字符串,算法要的是 number。直接 tonumber 失败会返回 nil,必须用兜底处理,否则后面算术运算会炸。

完整代码(可直接下载运行):

lua
-- 把任意来源的数值字符串转成 number,失败给默认值并报警
local function to_num(s, default)
    local n = tonumber(s)
    if n == nil then
        print("数值解析失败,使用默认值:", default, "原始:", s)
        return default
    end
    return n
end

local setpoint = to_num("350", 350)
print("目标值 =", setpoint)
-- 后续用它写 D 区或做 PID:plc.dw(200, setpoint)

要点:

  • tonumber("25.6") → 25.6;tonumber("abc") → nil(不是 0,也不会报错)。
  • 十六进制字符串用 tonumber("1A", 16) 指定进制。
  • type(n) == "number" 是判断「到底是不是数字」最可靠的办法。

验证方法:

  1. 把输入改成 "abc",日志应打印「数值解析失败,使用默认值: 350」而非崩溃。
  2. 把输入改成 "25.6",日志打印「目标值 = 25.6」。

注意事项:

  • tonumber 失败时返回 nil,不是 0;拿 nil 做 + 运算会直接抛错中断脚本。
  • 工业报文里常见 " 25.6 "(带空格),先用 string.match(s, "%d+%.?%d*") 抓数字再 tonumber 更稳(见字符串库例程)。
  • 浮点别用 == 直接比大小,用 math.abs(a-b) < 1e-6(见数学库例程)。

例程 2:用 pcall 兜住会出错的外设调用 ​

适用:同上 | 依赖模块:base com(可选)| 难度:★☆☆

场景:com.recv、com.send、读某个可能不存在的寄存器、解析坏 JSON 都可能抛异常。一旦顶层未捕获,整个脚本就挂了。用 pcall 包一层,出错时只记录、不中断。

完整代码(可直接下载运行):

lua
local ok, err = pcall(function()
    -- 任何可能出错的外设操作都放这里
    local data = com.recv(1000)     -- 等 1 秒收一帧
    local j = json.decode(data)     -- 坏数据会抛异常
    plc.dw(100, j.value)
end)

if not ok then
    print("本次处理失败,已跳过:", err)
    -- 不 return / 不崩溃,脚本继续往下走或结束
end

要点:

  • pcall(f) 返回 true, 返回值... 或 false, 错误信息。
  • 把「会出错的一小段」包进 pcall,而不是把整个脚本都包进去,否则真正的问题会被吞掉。
  • assert(cond, msg) 等价于 if not cond then error(msg) end,配合 pcall 使用。

验证方法:

  1. 让 com.recv 收到一段非 JSON 文本,json.decode 抛错:pcall 捕获,日志打印「本次处理失败,已跳过: ...」且脚本不挂。
  2. 收到正常 JSON,走通 plc.dw。

注意事项:

  • pcall 只捕获 Lua 错误,不捕获固件底层的硬件超时(那种通常自己返回错误码,不是抛异常)—— 别以为包了 pcall 就万事大吉。
  • pcall 里 return 只从匿名函数返回,不会跳出外层;要提前结束整段逻辑用 if not ok then return end。

例程 3:数据校验包装,统一返回 nil, err ​

适用:同上 | 依赖模块:base | 难度:★★☆

场景:写一个被多处调用的解析函数,约定「成功返回结果,失败返回 nil + 错误串」,调用方用 local v, e = parse(...) 判空即可,不用到处写 pcall。

完整代码(可直接下载运行):

lua
local function parse_temp(raw)
    if type(raw) ~= "string" then
        return nil, "输入必须是字符串"
    end
    local n = tonumber(raw)
    if n == nil then
        return nil, "无法解析为数字: " .. raw
    end
    if n < -50 or n > 150 then
        return nil, "温度超出合理范围: " .. raw
    end
    return n
end

local v, e = parse_temp("23.5")
if v then
    print("温度 =", v)
else
    print("解析失败:", e)
end

要点:

  • 约定 nil, err 返回风格与 pcall 的 false, err 一致,调用方处理简单。
  • type() 先挡住非字符串,再 tonumber,再范围校验,层层兜底。
  • 拼接错误信息用 ..,注意数字要先 tostring。

验证方法:

  1. 输入 "23.5" → 打印「温度 = 23.5」。
  2. 输入 "abc" → 打印「解析失败: 无法解析为数字: abc」。
  3. 输入 "200" → 打印「解析失败: 温度超出合理范围: 200」。

注意事项:

  • assert(parse_temp("abc")) 会在失败时抛错而非返回 nil;若想用返回值风格就别用 assert。
  • 错误串里拼接 nil/number 会抛错,拼接前确保是字符串(tostring)。

例程 4:现场常用脚本 内存回收.LPLUA(原文) ​

适用:同上 | 依赖模块:base(collectgarbage) | 难度:★☆☆

场景:设备跑久了内存只涨不跌,先要能看见「涨了多少、能不能回来」。这份脚本用 1000 个空表 把水位顶上去,再置 nil + collect 看它能不能回落(文件 内存回收.LPLUA),原文收录如下。

完整代码(内存回收.LPLUA 原文):

lua
collectgarbage("collect");--为了有干净的环境,先把可以收集的其他垃圾赶走先
local c1 = collectgarbage("count")
print("最开始,Lua的内存为",c1,"kb")

mtable = {["x"]="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",}--是本地变量
print("现在,声明1000个数组,并加到mtable 中")
for i=1,1000 do
    table.insert(mtable,{}) --不断的把新创建的数组,放到一个全局的变量中
end
print("现在内存为:",collectgarbage("count"),"kb")

mtable = nil --不再使用表了 ,要释放掉
print("调用收集…")
collectgarbage("collect")--回收内存
print("收集后,当前的Lua内存为",collectgarbage("count"),"kb")

要点:

  • collectgarbage("count") 返回当前 Lua 占用内存,单位 KB,带小数(不是整数),别拿 == 比它。
  • 开头先 "collect" 一次是为了拿到干净基线,否则第一次读数里混着上一轮的垃圾。
  • ⚠ 注释写着「是本地变量」,但 mtable = {…} 没有 local,它其实是全局变量。这份脚本靠的正是「全局 → 置 nil → 能被回收」这条路径;真按 local 写,脚本一结束变量本来就没了,反而看不出效果。
  • 全局表不清引用就永远占着内存 —— 这是设备上内存只涨不跌最常见的原因。
  • collectgarbage("collect") 是完整一轮 GC,在设备上会有一次短暂停顿;用它做演示没问题,但不要放进高频定时器回调里当常规手段。
  • mtable = nil 后面紧接 collectgarbage("collect") 是这类脚本的常见写法:com / tcp_client / http 这些对象句柄要等一次真正的 collect 才跑 __gc(http.LPLUA 里也是这么写的)。

验证方法:

  1. 下载运行,日志依次打印三个 KB 数:最开始 → 加了 1000 个表之后(明显上去)→ 置 nil 并收集之后(应回到接近最开始的水位)。
  2. 把 mtable = nil 那一行注掉再跑一次,最后一行仍会明显高于基线 —— 用这个对比直接证实「不清引用就回收不了」。

注意事项:

  • 只删了表的一部分元素、外层表还挂在全局上,回收不掉。
  • 只看 count 的绝对值:不同机型、不同已加载模块下基线差很多,只有同一次运行里的前后差有意义。
  • 把 collectgarbage("collect") 当成「防内存泄漏的保命药」周期调用:它只能回收已经断掉引用的对象,引用没断时再调也没用。

函数速查表 ​

下表列出本库全部函数与方法(含尚未单独成节的),点「说明」可跳转到对应小节。签名按通用记法:括号内按顺序列全参数,方括号里的可以省略(不传走默认值),... 表示还可继续传同类参数;h: 开头的是对象方法,先用库级函数拿回句柄再冒号调用。

方法与签名作用说明
base._VERSIONLua 版本号字符串(是变量不是函数,不要加括号调用)跳转
base.assert(...)断言条件成立,否则报错中断脚本本文开头
base.collectgarbage(…)手动触发内存垃圾回收本文开头
base.dofile(…)读取并立即执行一个 Lua 文件跳转
base.error(…)主动抛出一个错误跳转
base.getmetatable(...)取得一个值的元表跳转
base.ipairs(...)按 1、2、3… 顺序遍历表的整数下标(for 循环用)跳转
base.load(…)把字符串编译成函数(不执行)跳转
base.loadfile(…)读取文件并编译成函数(不执行)跳转
base.next(…)取表中的下一对键值(遍历的底层函数)跳转
base.pairs(...)遍历表的全部键值对(for 循环用)跳转
base.pcall(...)保护调用:被调函数出错时返回 false+错误信息,不中断脚本跳转
base.print(...)打印内容到日志/控制台本文开头
base.rawequal(...)比较两个值是否相等,不触发元方法跳转
base.rawget(...)读表元素,不触发 __index 元方法跳转
base.rawlen(…)取表或字符串的长度,不触发 __len 元方法跳转
base.rawset(...)写表元素,不触发 __newindex 元方法跳转
base.select(integer, ...)按序号取可变参数("#" 返回参数个数)跳转
base.setmetatable(…)设置一个值的元表跳转
base.tonumber(...)把值转成数字(可指定进制),转不了返回 nil跳转
base.tostring(...)把任意值转成字符串跳转
base.type(…)返回值的类型名(number/string/table/...)跳转
base.warn(string, ...)输出一条警告信息跳转
base.xpcall(...)带错误处理函数的保护调用,出错时先回调再返回跳转

base._VERSION ​

  • 作用:Lua 版本号字符串(是变量不是函数,不要加括号调用)
  • 形态:base._VERSION 不是可调用函数(不要加括号),按变量使用
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.dofile ​

  • 作用:读取并立即执行一个 Lua 文件
  • 调用形态:base.dofile(…)(库级函数)
  • 参数:无参数
  • 可用机型:软 PLC 模拟器 / 上位机

base.error ​

  • 作用:主动抛出一个错误
  • 调用形态:base.error(…)(库级函数)
  • 参数:无参数
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.getmetatable ​

  • 作用:取得一个值的元表
  • 调用形态:base.getmetatable(...)(库级函数)
  • 参数:1 个参数:第 1 个为任意(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.ipairs ​

  • 作用:按 1、2、3… 顺序遍历表的整数下标(for 循环用)
  • 调用形态:base.ipairs(...)(库级函数)
  • 参数:1 个参数:第 1 个为任意(必填)
  • 返回:3 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.load ​

  • 作用:把字符串编译成函数(不执行)
  • 调用形态:base.load(…)(库级函数)
  • 参数:无参数
  • 可用机型:软 PLC 模拟器 / 上位机

base.loadfile ​

  • 作用:读取文件并编译成函数(不执行)
  • 调用形态:base.loadfile(…)(库级函数)
  • 参数:无参数
  • 可用机型:软 PLC 模拟器 / 上位机

base.next ​

  • 作用:取表中的下一对键值(遍历的底层函数)
  • 调用形态:base.next(…)(库级函数)
  • 参数:无参数
  • 返回:2 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.pairs ​

  • 作用:遍历表的全部键值对(for 循环用)
  • 调用形态:base.pairs(...)(库级函数)
  • 参数:1 个参数:第 1 个为任意(必填)
  • 返回:4 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.pcall ​

  • 作用:保护调用:被调函数出错时返回 false+错误信息,不中断脚本
  • 调用形态:base.pcall(...)(库级函数)
  • 参数:1 个参数:第 1 个为任意(必填),其后还有可变参数
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.rawequal ​

  • 作用:比较两个值是否相等,不触发元方法
  • 调用形态:base.rawequal(...)(库级函数)
  • 参数:2 个参数:第 1 个为任意(必填)、第 2 个为任意(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.rawget ​

  • 作用:读表元素,不触发 __index 元方法
  • 调用形态:base.rawget(...)(库级函数)
  • 参数:1 个参数:第 2 个为任意(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.rawlen ​

  • 作用:取表或字符串的长度,不触发 __len 元方法
  • 调用形态:base.rawlen(…)(库级函数)
  • 参数:无参数
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.rawset ​

  • 作用:写表元素,不触发 __newindex 元方法
  • 调用形态:base.rawset(...)(库级函数)
  • 参数:2 个参数:第 2 个为任意(必填)、第 3 个为任意(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.select ​

  • 作用:按序号取可变参数("#" 返回参数个数)
  • 调用形态:base.select(integer, ...)(库级函数)
  • 参数:1 个参数:第 1 个为整数(必填),其后还有可变参数
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.setmetatable ​

  • 作用:设置一个值的元表
  • 调用形态:base.setmetatable(…)(库级函数)
  • 参数:无参数
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.tonumber ​

  • 作用:把值转成数字(可指定进制),转不了返回 nil
  • 调用形态:base.tonumber(...)(库级函数)
  • 参数:2 个参数:第 1 个为任意(必填)、第 2 个为整数(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.tostring ​

  • 作用:把任意值转成字符串
  • 调用形态:base.tostring(...)(库级函数)
  • 参数:1 个参数:第 1 个为任意(必填)
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.type ​

  • 作用:返回值的类型名(number/string/table/...)
  • 调用形态:base.type(…)(库级函数)
  • 参数:无参数
  • 返回:1 个值
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.warn ​

  • 作用:输出一条警告信息
  • 调用形态:base.warn(string, ...)(库级函数)
  • 参数:1 个参数:第 1 个为字符串(必填),其后还有可变参数
  • 返回:无
  • 可用机型:ESP32 机型 · 软 PLC 模拟器 / 上位机

base.xpcall ​

  • 作用:带错误处理函数的保护调用,出错时先回调再返回
  • 调用形态:base.xpcall(...)(库级函数)
  • 参数:无参数
  • 可用机型:软 PLC 模拟器 / 上位机

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研