lua应用场景
其实 Lua 的核心优势是轻量、高性能、易嵌入、语法简洁,这让它几乎成为 “嵌入式脚本” 的首选
- 游戏开发(Lua 最核心的应用领域)
Lua 是游戏行业的 “标配脚本语言”,几乎所有大型游戏引擎 / 项目都会用它做逻辑层开发,核心原因是:
- 轻量(解释器体积仅几十 KB),对游戏内存占用极低;
- 与 C/C++ 交互极其便捷(游戏引擎多由 C/C++ 编写),可无缝嵌入;
- 热更新友好(游戏上线后可通过 Lua 脚本更新逻辑,无需重新打包)。
- 嵌入式系统 / 物联网(IoT)
Lua 是嵌入式设备的 “理想脚本语言”,尤其适合智能硬件、路由器、工业控制等场景,原因是:
- 解释器占用资源极少(内存占用 < 100KB),适配低配硬件;
- 可直接调用底层 C 接口,操作硬件(如传感器、串口);
- 语法简单,非专业程序员也能快速编写控制逻辑。
- 应用程序扩展 / 插件开发
很多大型软件会把 Lua 作为 “扩展脚本语言”,让用户 / 开发者通过 Lua 自定义功能,核心价值是:
- 不影响主程序稳定性(Lua 脚本崩溃不会导致主程序挂掉);
- 无需重新编译主程序,即可扩展功能。
- 网络编程 / 服务器端(小众但实用)
Lua 也可用于轻量级服务端开发,核心优势是高性能、并发友好:
- 基于 OpenResty(Nginx + Lua 集成),可开发高性能的 API 网关、反向代理、Web 服务;
- 相比 Python/Node.js,OpenResty 处理高并发请求时性能更优(接近 Nginx 原生)。
windows安装lua
先去lua官网
https://www.lua.org/download.html
依次点击 binaries → Download → lua-5.4.2_Win64_bin.zip
然后下载之后把这个目录加到环境变量里面就好了
不想配环境变量的话,可以直接使用一件安装的工具
https://github.com/rjpcomputing/luaforwindows/releases
这个工具会自动安装lua5.1和luarocks以及一些常用第三方包
或者也可以安装openresty,反正我们最终的目的是进行将lua应用于nginx中
https://openresty.org/cn/download.html
hello world
运行一个lua文件也非常简单
创建一个hello.lua文件
print("hello world")
使用 lua hello.lua 就可以运行了
使用编辑器开发
如果要在goland里面写lua,如果需要语法提示,需要下载lua的插件
lua声明变量是不需要声明类型的,并且声明之后后期也可以修改类型,这一点和python是类似的
--这是注释
--直接定义的是全局变量
a = 1
b = "fengfeng"
c = 'zhidao' -- 单引号也可以
d = true
e = false
f = { 1, 2, 3 } -- 数组
g = { name = "fengfeng", age = a } -- 表
--也可以这样批量赋值
h, i, j = "a", 2, false
--使用print打印,type显示类型
print("a", a, type(a))
print("b", b, type(b))
print("c", c, type(c))
print("d", d, type(d))
print("e", e, type(e))
print("f", f, type(f))
print("g", g, type(g))
print("h i j", h, i, j)
--动态改类型也是可以的
a = "zhangsan"
print("a", a, type(a))
局部变量
使用local关键字声明局部变量,局部变量的作用范围是当前的代码块
do
--这是代码块
local a = 1
print("a", a)
end
print("a", a) -- 打印为nil
数组
Lua 无原生数组,通过 table + 数字索引 模拟,默认索引从 1 开始
# 符号的局限性:仅对 “连续数字索引” 有效,非连续数组需手动计算长度
local array = {"A", nil, "B", "C", "D", 1, 2, 3, 4}
print(array, type(array)) -- table
print(array[1]) -- A
print(array[-1]) -- 访问不存在的索引,nil
-- 获取数组长度
print(#array)
print(array[#array]) -- 4
--遍历数组
-- 方式1:按索引遍历(推荐,可控)
for i = 1, #array do
print(i, array[i])
end
-- 方式2:用 pairs 遍历(会遍历所有键值对,包括非连续索引) 遇到nil 跳过
for k, v in pairs(array) do
print("pairs 键:" .. k .. ",值:" .. v)
end
-- 方式3:用 ipairs 遍历(只遍历连续的数字索引,从 1 开始) 遇到nil break
for k, v in ipairs(array) do
print("ipairs 键:" .. k .. ",值:" .. v)
end
表
这是 Lua 最核心、最强大的数据结构 ——Lua 里几乎所有复杂数据类型(数组、字典、对象等)都是基于 table 实现的
table 是一种键值对(key-value) 集合,类似 Python 的字典(dict),但比字典更灵活:
- 键(key)可以是除 nil 外的任意类型(数字、字符串、布尔、甚至另一个 table);
- 值(value)可以是任意类型(数字、字符串、函数、table 等);
- table 是动态的,可随时添加 / 删除键值对;
- table 是引用类型(赋值、传参时传递的是地址,而非拷贝)。
定义table
-- 方式1:空 table
local t1 = {}
-- 方式2:初始化键值对(推荐)
-- ① 隐式键(数字索引,默认从 1 开始,对应之前讲的数组)
local t2 = {"Lua", "Python", "Java"} -- 等价于 t2[1]="Lua", t2[2]="Python", t2[3]="Java"
-- ② 显式键(任意类型)
local t3 = {
name = "张三", -- 等价于 t3["name"] = "张三"(字符串键)
age = 20, -- 数字值
is_student = true, -- 布尔值
[100] = "分数", -- 数字键(显式写法)
["hobby-list"] = {"读书", "运动"} -- 字符串键(含特殊字符)+ 值为 table
}
-- ③ 混合键(数组 + 字典)
local t4 = {
10, 20, 30, -- 隐式数字键:1、2、3
name = "李四", -- 显式字符串键
[false] = "布尔键" -- 显式布尔键
}
访问table元素
local t = {name = "张三", age = 20, [1] = "hello"}
-- 访问方式1:点语法(仅适用于 字符串键且符合标识符规则,如字母/数字/下划线)
print(t.name) -- 输出:张三
print(t.age) -- 输出:20
-- print(t.1) -- 报错!点语法不能用于数字键
-- 访问方式2:方括号语法(通用,适用于所有键类型)
print(t["name"]) -- 输出:张三
print(t[1]) -- 输出:hello
print(t["age"]) -- 输出:20
-- 访问不存在的键,返回 nil
print(t.gender) -- 输出:nil
修改table元素
local t = {name = "张三"}
-- 添加新键值对
t.age = 20 -- 字符串键
t[100] = "满分" -- 数字键
t["hobby"] = "跑步" -- 字符串键(显式)
-- 修改已有键值对
t.name = "李四"
print(t.name) -- 输出:李四
-- 删除键值对(赋值为 nil)
t.age = nil
print(t.age) -- 输出:nil
注意事项
- 键的隐式转换:数字键和字符串键是不同的(如
t[1]≠t["1"]); - # 符号的局限性:仅对连续数字索引有效,字典型 table 别用
#求长度; - nil 的影响:
ipairs()遇到 nil 会停止遍历,如需 “空元素” 可用 0 / 空字符串占位; - 引用赋值:避免误以为
t2 = t1是拷贝,修改 t2 会影响 t1。
条件判断
Lua 的 if 语句用于根据条件执行不同代码块,核心语法和其他语言类似,但没有 elif 关键字,需用 elseif(连写)替代,且结束必须用 end。
-- 单分支:只有 if
local score = 85
if score >= 60 then
print("及格")
end
-- 双分支:if + else
if score >= 90 then
print("优秀")
else
print("非优秀")
end
-- 多分支:if + elseif + else(注意是 elseif,不是 elif/else if)
if score >= 90 then
print("优秀")
elseif score >= 80 then
print("良好")
elseif score >= 60 then
print("及格")
else
print("不及格")
end
循环语句
Lua 提供 3 种核心循环:while、for(数值 for / 泛型 for)、repeat...until,覆盖所有循环场景。
while循环
语法:while 条件 do ... end,满足条件时执行循环体,适合循环次数不确定的场景。
-- 示例:计算 1-10 的和
local sum = 0
local i = 1
while i <= 10 do
sum = sum + i
i = i + 1 -- 必须手动更新循环变量,否则死循环
end
print(sum) -- 输出:55
for循环
Lua 的 for 分两种:数值 for(遍历数字范围)、泛型 for(遍历集合,如 table)。
数值 for(核心格式:for 变量=起始值, 结束值, 步长 do … end)
-- 示例1:默认步长 1,遍历 1-5
for i = 1, 5 do
print(i) -- 输出:1 2 3 4 5
end
-- 示例2:指定步长 2,遍历 1-10 的奇数
for i = 1, 10, 2 do
print(i) -- 输出:1 3 5 7 9
end
-- 示例3:步长为负数,倒序遍历
for i = 5, 1, -1 do
print(i) -- 输出:5 4 3 2 1
end
泛型 for(遍历集合,结合 ipairs/pairs)
-- 示例1:遍历数组型 table(ipairs:连续数字索引)
local arr = {"Lua", "Python", "Java"}
for k, v in ipairs(arr) do
print("索引:", k, "值:", v) -- 输出:1 Lua / 2 Python / 3 Java
end
-- 示例2:遍历字典型 table(pairs:所有键值对)
local dict = {name = "张三", age = 20}
for k, v in pairs(dict) do
print("键:", k, "值:", v) -- 输出:name 张三 / age 20(顺序不固定)
end
repeat…until 循环(直到型循环)
语法:repeat ... until 条件,先执行循环体,再判断条件(至少执行一次),适合 “先做后判断” 的场景。
-- 示例:输入数字,直到输入大于 100 为止
local num = 0
repeat
print("请输入一个数字:")
num = tonumber(io.read()) -- 读取控制台输入并转为数字
until num > 100 -- 条件满足时退出循环
print("你输入了大于 100 的数字:", num)
流程跳转
用于中断或跳转代码执行,Lua 支持 break、return、goto(标签跳转)。
- 不等于用~=
- lua没有continue
- 慎用goto
-- 示例:用 goto 实现循环
local i = 1
::loop:: -- 定义标签
if i > 5 then
goto end_loop -- 跳转到结束标签
end
print(i)
i = i + 1
goto loop -- 跳回循环标签
::end_loop:: -- 结束标签
print("循环结束")
lua函数
Lua 函数有两种定义形式,核心语法是 function 函数名(参数) ... end,结束必须用 end 界定代码块。
函数声明
-- 方式1:标准定义(全局函数)
function add(a, b)
return a + b -- 返回值,可多个
end
-- 方式2:标准定义(局部函数)
local function add1(a, b)
return a + b -- 返回值,可多个
end
-- 方式3:赋值式定义(更灵活,可定义局部函数)
local sub = function(a, b)
return a - b
end
-- 调用函数(和其他语言一致)
local sum = add(3, 5)
local sum1 = add1(3, 5)
local diff = sub(10, 4)
print(sum, sum1, diff) -- 输出:8 8 6
函数参数
Lua 不检查调用时的参数个数,参数不匹配时会自动补 nil 或忽略多余参数;
参数不匹配
-- 示例:参数个数不匹配
function test(a, b)
print(a, b)
end
test(1) -- 输出:1 nil(第二个参数补 nil)
test(1, 2, 3) -- 输出:1 2(第三个参数被忽略)
默认参数:手动实现(Lua 无原生默认参数)
-- 示例:给参数设置默认值
function greet(name)
-- 如果 name 是 nil,赋值为 "游客"
name = name or "游客"
print("你好," .. name)
end
greet() -- 输出:你好,游客
greet("张三") -- 输出:你好,张三
可变参数:接收任意个数的参数
用 ...(三点)表示可变参数,可通过 {...} 转为 table 遍历,或用 select() 获取参数个数 / 指定参数。
-- 示例1:求和任意个数的数字
function sum(...)
local args = {...} -- 把可变参数转为 table
local total = 0
for _, v in ipairs(args) do
total = total + v
end
return total
end
print(sum(1,2,3)) -- 输出:6
print(sum(10,20,30)) -- 输出:60
-- 示例2:用 select 获取参数信息
function test(...)
print(select("#", ...)) -- 获取参数个数:输出 3
print(select(2, ...)) -- 获取第 2 个及之后的参数:输出 2 3
end
test(1,2,3)
函数值
Lua 函数可以返回多个值,调用时可接收全部或部分返回值,这是和很多语言的重要区别。
-- 定义返回多个值的函数
function get_user()
return "张三", 20, "男" -- 返回 3 个值
end
-- 接收全部返回值
local name, age, gender = get_user()
print(name, age, gender) -- 输出:张三 20 男
-- 只接收部分返回值(多余的会被忽略)
local name_only = get_user()
print(name_only) -- 输出:张三
-- 忽略某个返回值(用 _ 占位)
local _, age_only = get_user()
print(age_only) -- 输出:20
函数的高级用法
函数作为值
Lua 中函数是 “第一类值”,可以赋值给变量、作为参数传递、作为 table 的元素,这是实现回调、闭包的基础。
function add(a, b)
return a + b
end
function sub(a, b)
return a - b
end
-- 1. 函数赋值给变量
local f = add
print(f(4,5)) -- 输出:9
-- 2. 函数作为 table 的元素(模拟对象方法)
local user = {
name = "李四",
say = function(self)
print("我是" .. self.name)
end
}
user:say() -- 输出:我是李四(冒号自动传 self)
-- 3. 函数作为参数(回调函数)
function calculate(a, b, func)
return func(a, b)
end
local result = calculate(10, 5, sub) -- 传入 sub 函数
print(result) -- 输出:5
闭包
闭包是 “可以访问外部作用域变量的函数”,即使外部作用域已结束,闭包仍能访问这些变量,是 Lua 实现私有变量、工厂函数的核心。
-- 示例:工厂函数,生成累加器
function create_counter(init)
local count = init or 0 -- 外部作用域变量
-- 闭包函数:访问外部的 count
return function()
count = count + 1
return count
end
end
-- 创建两个独立的累加器
local counter1 = create_counter(0)
local counter2 = create_counter(10)
print(counter1()) -- 输出:1
print(counter1()) -- 输出:2
print(counter2()) -- 输出:11
print(counter2()) -- 输出:12
递归
函数可以调用自身(递归),需注意设置终止条件,避免死循环。
-- 示例:递归计算阶乘
function factorial(n)
if n == 1 then -- 终止条件
return 1
end
return n * factorial(n-1) -- 递归调用
end
print(factorial(5)) -- 输出:120(5*4*3*2*1)
表方法和对象方法
- 表方法(Table Method)
定义:直接绑定在表上的函数,是 Lua 最基础的 “方法” 形式,本质是 “表的键值对,值为函数”。
- 表是 Lua 最核心的数据结构,任何函数都可以作为表的一个字段(键),这个函数就称为 “表方法”;
- 表方法不强制依赖
self,可独立调用,也可结合self使用。
- 对象方法(Object Method)
定义:模拟面向对象编程时,给 “对象(本质还是表)” 绑定的、依赖 self 访问对象属性的表方法。
- Lua 中 “对象” 是通过表模拟的(表包含属性 + 方法),“对象方法” 就是这个模拟对象的方法;
- 必须依赖
self(指向对象本身),用于访问对象的属性,是表方法的 “面向对象化” 用法。
-- 定义一个工具表,存放通用函数(纯表方法)
local utils = {}
-- 表方法1:无参数,纯功能
utils.print_hello = function()
print("Hello, Lua!")
end
-- 表方法2:有参数,处理外部数据
utils.add = function(a, b)
return a + b
end
-- 调用:只用 . 调用,无需 self
utils.print_hello() -- 输出:Hello, Lua!
print(utils.add(1, 2)) -- 输出:3
把表当作 “对象”,表中既有属性(如 name/age),又有方法(依赖 self 访问属性),这就是 “对象方法”:
-- 把表当作“对象”,包含属性和方法
local person = {
name = "fengfeng",
age = 21
}
-- 定义对象方法:必须接收 self(指向对象本身),访问对象属性
function person:say_hello() -- 定义时用 :,自动加 self 参数
print("我是" .. self.name .. ",今年" .. self.age .. "岁")
end
-- 定义对象方法:修改对象属性
function person:grow_up()
self.age = self.age + 1
print(self.name .. "长大了,现在" .. self.age .. "岁")
end
-- 调用对象方法:用 :,自动传 self(即 person)
person:say_hello() -- 输出:我是fengfeng,今年21岁
person:grow_up() -- 输出:fengfeng长大了,现在22岁
lua元表
这是 Lua 中最核心也最灵活的特性之一 —— 元表本质是给普通 table 附加 “自定义行为” 的特殊 table,比如让 table 支持加减运算、自定义索引查找规则等
- 元表(Metatable):一个特殊的 table,用来定义另一个 table(被元表修饰的 table 称为 “原表”)的特殊行为;
- 元方法(Metamethod):元表中以
__开头的键(比如__add、__index),对应原表的自定义行为(比如__add定义加法运算); - 核心函数:
setmetatable(t, mt):给 tablet设置元表mt(返回t);getmetatable(t):获取 tablet的元表(返回元表或 nil)。
__index(索引查找)
当访问原表中不存在的键时,Lua 会去元表的 __index 中查找,这是 Lua 实现继承、默认值的核心。
__index 可以是 table(优先查找该 table 的键),也可以是函数(自定义查找逻辑)。
-- 示例1:__index 为 table(继承的底层逻辑)
local proto = {name = "默认名称", age = 18} -- 原型 table
local obj = {gender = "男"} -- 原表
-- 给 obj 设置元表,__index 指向 proto
setmetatable(obj, {__index = proto})
-- 访问 obj 不存在的键,自动去 proto 找
print(obj.name) -- 输出:默认名称(obj 无 name,从 proto 取)
print(obj.age) -- 输出:18(同理)
print(obj.gender)-- 输出:男(obj 自身有,直接取)
-- 示例2:__index 为函数(自定义查找逻辑)
local t = {}
setmetatable(t, {
__index = function(table, key) -- table=原表,key=访问的键
return "键 " .. key .. " 不存在,返回默认值"
end
})
print(t.abc) -- 输出:键 abc 不存在,返回默认值
__newindex(索引赋值)
当给原表中不存在的键赋值时,Lua 会调用元表的 __newindex,而非直接给原表添加键。
- 用法和
__index类似,可用于限制赋值、记录赋值日志、重定向赋值目标。
-- 示例:禁止给原表添加新键
local t = {name = "张三"}
setmetatable(t, {
__newindex = function(table, key, value)
print("赋值操作:表 =", table, "键 =", key, "值 =", value)
-- 使用 rawset 绕开元方法,直接给原表赋值(避免递归触发 __newindex)
rawset(table, key, value)
end
})
t.age = 20 -- 报错:禁止给表添加新键:age(t 无 age 键,触发 __newindex)
t.name = "李四" -- 正常执行(t 有 name 键,不触发 __newindex)
__add/__sub/__mul 等
通过元方法自定义原表的算术运算,比如让两个 table 支持加法。常用算术元方法:__add(+)、__sub(-)、__mul(*)、__div(/)、__eq(==)。
-- 示例:让 table 表示向量,支持加法运算
local vec1 = {x = 1, y = 2}
local vec2 = {x = 3, y = 4}
-- 定义元表(包含 __add 元方法)
local vec_mt = {
__add = function(a, b) -- a=左操作数,b=右操作数
return {x = a.x + b.x, y = a.y + b.y}
end
}
-- 给两个向量设置元表
setmetatable(vec1, vec_mt)
setmetatable(vec2, vec_mt)
-- 向量加法(触发 __add)
local vec3 = vec1 + vec2
print(vec3.x, vec3.y) -- 输出:4 6
__call(把 table 当作函数调用)
当原表被当作函数调用时(比如 t()),触发 __call 元方法,可实现 “可调用的对象”。
local t = {name = "Lua"}
setmetatable(t, {
__call = function(table, ...) -- table=原表,...=调用时的参数
print("调用了 table,参数:", ...)
print("table 的 name:", table.name)
end
})
-- 把 table 当作函数调用(触发 __call)
t(10, 20)
-- 输出:
-- 调用了 table,参数:10 20
-- table 的 name:Lua
__tostring(自定义打印格式)
当用 print() 打印原表时,触发 __tostring 元方法,替代默认的 table: 0xXXXXXXX 格式。
local person = {name = "张三", age = 20}
setmetatable(person, {
__tostring = function(table)
return "Person: " .. table.name .. "(" .. table.age .. "岁)"
end
})
print(person) -- 输出:Person: 张三(20岁)(而非默认的内存地址)
lua的模块与包
- 模块:一个
.lua文件就是一个模块,通常把一组相关的函数、变量、数据封装在这个文件里,对外暴露指定接口; - 包:多个功能相关的模块组成的集合(比如放在同一个目录下),Lua 中包的管理主要靠目录结构和
package.path配置。
Lua 模块的本质是:把模块内的功能(函数、变量)挂载到一个 table 上,最后返回这个 table,让外部能访问到指定功能(未挂载的则为私有)。
定义模块
-- 定义模块内的私有函数(仅模块内可用,不导出)
local function check_num(num)
return type(num) == "number"
end
-- 定义模块的公开接口(挂载到 table 上)
local mymath = {}
-- 公开函数:加法
function mymath.add(a, b)
if not (check_num(a) and check_num(b)) then
error("参数必须是数字")
end
return a + b
end
-- 公开函数:乘法
function mymath.mul(a, b)
if not (check_num(a) and check_num(b)) then
error("参数必须是数字")
end
return a * b
end
-- 公开变量
mymath.PI = 3.1415926
-- 必须返回这个 table(核心!否则外部无法访问)
return mymath
加载模块
外部文件通过 require("模块名") 加载模块,这是 Lua 加载模块的核心方式,有几个关键特性:
- 模块名称不要和内置模块重名
- 如果有目录关系,require的时候以 . 分隔
-- 加载模块(模块名不带 .lua 后缀)
local mm = require("mymath")
-- 调用模块的公开函数
print(mm.add(3, 5)) -- 输出:8
print(mm.mul(4, 6)) -- 输出:24
print(mm.PI) -- 输出:3.1415926
-- 无法访问模块内的私有函数(check_num)
-- print(mm.check_num(5)) -- 报错:attempt to index field 'check_num' (a nil value)
模块查找路径
require 会根据 package.path 配置的路径查找模块文件,默认路径包含当前目录(./?.lua);
如果模块不在默认路径下,需要手动修改 package.path:
-- 查看当前模块查找路径
print(package.path)
-- 添加自定义路径(比如模块放在 lib 目录下)
package.path = package.path .. ";./lib/?.lua"
-- 加载 lib 目录下的模块
local utils = require("utils")
包的管理
当模块数量多的时候,需要按功能组织成 “包”(目录),核心是通过 init.lua 实现目录作为模块。
比如创建一个 utils 包,包含 string.lua 和 math.lua 两个模块:
project/
├── main.lua # 主程序
└── utils/ # 包目录
├── init.lua # 包的入口文件(关键)
├── string.lua # 字符串工具模块
└── math.lua # 数学工具模块
init.lua
-- utils/init.lua
local utils = {}
-- 加载子模块并挂载到 utils 上
utils.string = require("utils.string")
utils.math = require("utils.math")
-- 也可以导出常用函数到顶层
function utils.trim(str)
return utils.string.trim(str)
end
return utils
使用包
-- 加载 utils 包(实际加载 utils/init.lua)
local utils = require("utils")
-- 调用子模块的函数
print(utils.string.trim(" hello lua ")) -- 输出:hello lua
print(utils.math.square(5)) -- 输出:25
-- 调用顶层导出的函数
print(utils.trim(" test ")) -- 输出:test
lua内置库
lua内置的库,直接require就可以用
math库
--取整
print(math.floor(3.9)) -- 向下取整 3
print(math.ceil(3.1)) -- 向上取整 4
print(math.modf(3.1)) -- 拆分整数和小数部分,返回两个值(整数 + 小数) 3 0.1
--绝对值
print(math.abs(-5))
--幂运算
print(2 ^ 3)
--随机数
math.randomseed(os.time()) -- 初始化随机种子
print(math.random()) -- 输出类似:0.789456
print(math.random(100)) -- 输出 1~100 之间的整数
print(math.random(10, 20)) -- 输出 10~20 之间的整数
-- 生成 [0,10) 之间的浮点数
local float_rand = math.random() * 10
print(float_rand) -- 输出类似:6.2345
-- 最大值,最小值
print(math.max(1, 5, 3, 9)) -- 输出:9
print(math.min(-2, 0, 4, -5)) -- 输出:-5
-- 去余数
print(math.fmod(7, 3)) -- 输出:1
print(7 % 3) -- 输出:1
print(math.fmod(-7, 3)) -- 输出:-1(% 运算符在Lua中结果符号与y一致,fmod与x一致)
string库
覆盖字符串查找、替换、截取、匹配(正则)等所有字符串操作:
local str = require("string") -- 也可直接用全局 string,或字符串对象方法
-- 核心功能(两种调用方式:string.xxx 或 str:xxx)
local s = " hello lua "
str1 = string.format("我的名字是:%s 我的年龄是:%d, 面积是:%.2f", "fengfeng", 21, 3.1415)
print(str1) -- 我的名字是:fengfeng 我的年龄是:21, 面积是:3.14
print(str.sub(s, 3, 7)) -- 截取子串(索引 3-7)→ "hello"
print(str.find(s, "lua")) -- 查找子串位置 → 9 11
print(string.gsub(s, "lua", "python")) -- 替换 → " hello python "
print(string.match(s, "%a+")) -- 模式匹配(字母)→ "hello"
-- 字符串对象方法(更简洁)
print(s:upper()) -- 转大写 → " HELLO LUA "
print(s:len()) -- 长度 → 10
模式匹配
Lua 没有独立的正则表达式库,而是内置了轻量的模式匹配,核心差异如下:
| 功能 | 标准正则 | Lua 模式匹配 | 说明 |
|---|---|---|---|
| 字母匹配 | [a-zA-Z] | %a | Lua 用 % 代替 `` 转义 |
| 数字匹配 | \d | %d | |
| 任意字符 | . | . | 一致 |
| 重复匹配(1 + 次) | + | + | 一致 |
| 重复匹配(0 + 次) | * | * | 一致 |
| 匹配开头 | ^ | ^ | 一致 |
| 匹配结尾 | $ | $ | 一致 |
| 非贪婪匹配 | +?/*? | 无(默认贪婪) | Lua 仅支持贪婪匹配 |
| 分组捕获 | (...) | (...) | 一致,但不支持非捕获组 |
| 反义匹配(非字母) | [^a-zA-Z] | %A | 大写字母表示 “反义” |
-- 1. 提取字符串中的第一个数字
local s1 = "价格:99元"
local num = string.match(s1, "%d+")
print(num) -- 输出:99
local s = "name=张三,age=25"
-- 模式中的两个()分别捕获 键 和 值
local key, value = string.match(s, "(%a+)=([^,]+)")
print("键:", key) -- 输出:键:name
print("值:", value) -- 输出:值:张三
-- 3. 匹配以特定字符开头/结尾的字符串
local s3 = "hello.lua"
local prefix = string.match(s3, "^%a+") -- 匹配开头的字母
local suffix = string.match(s3, "%a+$") -- 匹配结尾的字母
print(prefix, suffix) -- 输出:hello lua
table库
补充 table 的通用操作(Lua 原生 table 仅支持 # 取长度,复杂操作需库函数):
local tbl = require("table")
-- 核心功能
local t = { 3, 1, 2 }
table.sort(t) -- 默认升序
print(table.concat(t, ",")) --1,2,3
table.insert(t, 10) -- 插入元素 在最后插入元素 10
print(table.concat(t, ",")) --1,2,3,10
table.insert(t, 2, 99) -- 插入元素(位置 2 插入 99)
print(table.concat(t, ",")) -- 1,99,2,3,10
--移除最后一个元素并返回
print(table.remove(t)) -- 10
--指定位置移除
table.remove(t, 2)
print(table.concat(t, ",")) -- 1,2,3
排序
sort默认是升序,如果需要降序,就需要自定义排序函数了
-- 数字排序
local nums = {5, 2, 9, 1, 7}
table.sort(nums) -- 原位排序,无返回值
print("数字升序:", table.concat(nums, ", ")) -- 输出:1, 2, 5, 7, 9
-- 字符串排序(按字符编码,中文按 Unicode 编码)
local fruits = {"香蕉", "苹果", "橙子", "葡萄"}
table.sort(fruits)
print("字符串排序:", table.concat(fruits, ", ")) -- 输出:橙子, 苹果, 葡萄, 香蕉
-- 示例1:数字降序排序
local nums = {5, 2, 9, 1, 7}
table.sort(nums, function(a, b)
-- a > b 时返回 true → a 排在 b 前面(降序)
return a > b
end)
print("数字降序:", table.concat(nums, ", ")) -- 输出:9, 7, 5, 2, 1
-- 示例2:按字符串长度排序
local words = {"lua", "python", "java", "c"}
table.sort(words, function(a, b)
-- 长度短的排在前面
return #a < #b
end)
print("按长度排序:", table.concat(words, ", ")) -- 输出:c, lua, java, python
-- 示例3:按表的自定义属性排序(复杂数据)
local students = {
{name = "张三", score = 85},
{name = "李四", score = 92},
{name = "王五", score = 78}
}
-- 按分数降序排序
table.sort(students, function(a, b)
return a.score > b.score
end)
-- 打印排序结果
for i, s in ipairs(students) do
print(i, s.name, s.score)
end
-- 输出:
-- 1 李四 92
-- 2 张三 85
-- 3 王五 78
os库
获取系统信息、执行系统命令、处理时间等
print(os.date("%Y-%m-%d %H:%M:%S")) -- 当前时间 → 2026-01-04 12:00:00
print(os.time()) -- 时间戳(秒)→ 1735996800
print(os.date("%Y-%m-%d %H:%M:%S", 1767882504)) -- 将时间戳转换为时间字符串
print(os.getenv("PATH")) -- 获取环境变量
os.execute("chcp 65001") -- 解决windows执行命令输出乱码问题
os.execute("dir") -- 执行系统命令(Linux)/ dir(Windows)
os.exit(0) -- 退出程序(0=正常退出)
io库
处理文件的读写、打开 / 关闭,分为 “简单 IO” 和 “文件句柄 IO”:
local io = require("io")
-- 1. 简单IO(一次性读写)
--print("请输入:")
io.write("请输入:") -- print默认是有一个换行的
io.flush()
data = io.read()
print("你输入的内容:", data)
-- 2. 文件句柄IO(推荐,可控性强)
local f = io.open("test.txt", "w") -- 打开文件(w=写入,r=读取,a=追加)
if f then
f:write("hello lua\n") -- 写入内容
f:close() -- 关闭文件
end
-- 读取文件
local f = io.open("test.txt", "r")
if f then
local content = f:read("*a") -- 读取所有内容
print(content) -- 输出:hello lua
f:close()
end
io.read() 的参数主要是模式字符串,分为 “通配符模式” 和 “数字模式” 两类,先讲最常用的通配符模式:
| 参数(模式) | 含义 | 示例场景 |
|---|---|---|
"*l" | 读取一行(默认模式),不包含末尾的换行符;遇到文件尾返回 nil | 读取用户输入的一行文本 |
"*L" | 读取一行,包含末尾的换行符;遇到文件尾返回 nil 5.3才有 | 保留换行符的文本读取 |
"*a" | 读取全部内容(从当前位置到文件尾),适合读取小文件 | 一次性读取整个文件内容 |
"*n" | 读取一个数字(整数 / 浮点数),自动跳过开头的空白字符;非数字返回 nil | 读取用户输入的数字 |
增强内置库
如果你直接在官网下载的lua可执行文件,内置库你就只能用上面那些
如果你是用的一键安装的那种,那么会有很多库可以用,比如json、re、socket
json库
local json = require("json")
--表转json
user = {
name="fengfeng",
age=12,
info={
addr="长沙"
}
}
json_str = json.encode(user)
print(json_str) -- {"info":{"addr":"长沙"},"name":"fengfeng","age":12}
--json转表
tb = json.decode('{"info":{"addr":"长沙"},"name":"fengfeng","age":12}')
print(tb.name) -- fengfeng
print(tb.info.addr) -- 长沙
re正则库
re = require("rex_pcre")
-- 忽略大小写
print(re.match("abcd", "abcd"))
print(re.match("ABCD", "abcd", 0, "i"))
print(re.match("ABCD", "(?i)abcd"))
-- 匹配 "姓名:年龄" 格式,分组提取姓名和年龄
local str = "张三:25\n李四:30"
local name, age = re.match(str, "(\\S+?):(\\d+)")
print(name, age) -- 张三 25
-- 全局匹配
local str = "联系信息:13812345678(手机),test@example.com(邮箱),123456(无效),abc@def(无效)"
local pattern = "((\\d{11})|([\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}))"
-- 全局迭代匹配所有符合规则的内容
print("匹配到的有效联系信息:")
for data, _, _ in re.gmatch(str, pattern) do
print("data:", data)
end
--非贪婪模式
local str = "<DIV>张三</div><p>李四</P><span>王五</span>"
-- 正则说明:
-- <([a-z]+)> :匹配标签名(如div、p),分组1
-- (.*?) :非贪婪匹配标签内的文本(避免匹配到最后一个</>)
-- </\\1> :匹配闭合标签(\\1引用分组1的标签名,保证前后标签一致)
-- i :模式修饰符(忽略大小写)
local pattern = "(?i)<([a-z]+)>(.*?)</\\1>" -- 修饰符写在pattern末尾
print("\n匹配到的标签内容:")
for tag, content in re.gmatch(str, pattern) do
print("标签:", tag, "内容:", content)
end
--正则分隔
local str = "张三123-李四456;王五789,赵六000"
local split_iter = re.split(str, "[-;,]")
print("遍历迭代器获取分割结果:")
for part in split_iter do
print(part)
end
string增强库
这个地方有的人会有误解,为什么不能直接require用
因为这种增强库是以扩展原库的方式扩展的
require("strbuf")
require("list")
require("string_ext")
print(string.split("1,2,3,4,5", ",")) -- {1=1,2=2,3=3,4=4,5=5}
print(string.trim(" 前后都有空格 ")) -- 前后都有空格
table增强库
require("table_ext")
t1 = table.sort({1, 5, 3, 2, 4}) -- 默认升序
for i, v in ipairs(t1) do
io.write(v .. "\t")
end
print()
t2 = table.sort({1, 5, 3, 2, 4}, function(a, b)
return a > b -- 降序
end)
for i, v in ipairs(t2) do
io.write(v .. "\t")
end
print()
print(table.size(t2)) -- 5
-- 获取索引
user = {
name="fengfeng", age=12
}
keys = table.indices(user)
for _, v in pairs(keys) do
print(v)
end
-- clone
u1 = table.clone(user)
u1.name="zhangsan"
print(u1.name)
print(user.name)
lua操作redis
lua
在openresty环境下,配置nginx.conf
location /test_redis {
content_by_lua_file lua/test_resty_redis.lua;
}
创建test_resty_redis.lua
-- 加载 OpenResty 内置的 redis 库
local redis = require("resty.redis")
-- 1. 创建 redis 客户端
local client = redis:new()
client:set_timeout(1000) -- 超时时间(1秒)
-- 2. 连接 Redis
local ok, err = client:connect("127.0.0.1", 6379)
if not ok then
ngx.say("Redis 连接失败:", err)
return
end
-- 有密码则认证
-- local ok, err = client:auth("your_redis_pwd")
-- if not ok then ngx.say("密码错误:", err) return end
-- 3. 执行操作(和普通 Lua 语法略有不同)
client:set("name", "OpenResty 测试")
local name, err = client:get("name")
ngx.header.content_type = "text/html; charset=utf-8"
ngx.say("获取 name:", name)
-- 4. 放入连接池(关键:复用连接,提升性能)
client:set_keepalive(10000, 100) -- 空闲 10 秒,最多 100 个连接
-- 不要调用 quit(),否则连接池失效
启动nginx,然后访问 127.0.0.1/test_redis 就可以看到redis的值了