Lua 简介

Lua 的历史与发展

⭐⭐ 起源 (1993)

Lua的首个版本发布于1993年最初是为游戏行业设计的旨在提供一个易于嵌入应用程序中的轻量级脚本语言

⭐⭐ Lua 1.x系列

在1.x系列中Lua逐渐增加了更多的功能并且开始吸引更多的开发者关注

⭐⭐ Lua 2.x系列

该系列版本引入了面向对象编程的支持并且增强了其作为通用脚本语言的功能

⭐⭐ Lua 3.x系列 (1996)

Lua 3.0是一个重要的里程碑它完全重写了之前的版本简化了API并移除了对宿主程序的依赖这次更新也引入了一些新的特性如尾调用优化

⭐⭐ Lua 4.x系列 (1999)

Lua 4.0继续发展增加了一些新特性如模块支持等但这个版本并没有广泛传播

⭐⭐ Lua 5.x系列 (2003)

  • Lua 5.0引入了垃圾回收机制并且进一步改进了语言的设计从这个版本开始Lua变得更为成熟稳定
  • Lua 5.1 (2005) 是一个非常稳定的版本它改善了性能并且增强了错误处理机制
  • Lua 5.2 (2012) 增加了对长整型的支持并且改进了错误报告机制
  • Lua 5.3 (2014) 改进了内存管理并且支持64位整数和尾调用优化
  • Lua 5.4 (2020) 引入了新的特性如模块加载器尾调用优化以及对Unicode的支持
  • Lua 5.5 (2023) 是最新的稳定版本之一继续改进性能和安全性并添加了新的语言特性

⭐⭐ 版本分支

  • LuaJIT (2005)这是一个即时编译器Just-In-Time Compiler可以显著提高Lua代码的执行速度LuaJIT支持Lua 5.1语法并且可以运行在多种平台上
  • Lua 5.1这是Lua的一个非常流行且稳定的版本许多现有的Lua应用和库都是基于此版本开发的
  • 其他分支还有一些其他分支和变种例如LuaRt它是专门为实时操作系统设计的Lua版本

Lua 的应用领域

⭐⭐ 游戏开发

Lua 因其轻量级易嵌入的特性在游戏开发中非常受欢迎它可以用于编写游戏逻辑AI配置文件等而且很多游戏引擎都支持 Lua如 Corona SDKLÖVE 和一些专有的游戏引擎
著名的游戏如魔兽世界侠盗猎车手系列文明系列等都使用了 Lua

⭐⭐ Web 开发

Lua 也可以用于 Web 应用程序的开发尤其是在需要高性能的情况下例如OpenResty 是一个基于 Nginx 和 Lua 的平台常用于构建高性能的 Web 应用和服务

Lua 在网络编程方面也有应用比如 LuaSocket 库提供了网络通信的功能

⭐⭐ 嵌入式系统

由于 Lua 的轻量级特性它可以被嵌入到各种嵌入式设备中用于设备管理和控制

Lua 也被用于物联网(IoT)设备上帮助实现简单的脚本控制

⭐⭐ 配置文件

Lua 的简单语法使其非常适合用来编写配置文件有些软件和应用程序选择 Lua 作为配置语言因为它既强大又容易理解

⭐⭐ 桌面应用

Lua 可以用来编写桌面应用程序的脚本特别是在需要快速开发或原型设计的时候

⭐⭐ 科学计算与工程

在某些科学计算和工程应用中Lua 也被用来处理数据和自动化任务

⭐⭐ 图形用户界面(GUI)

Lua 可以用来创建 GUI 应用程序尽管这并不是它的强项但仍有一些库如 LÖVE 提供了图形和GUI支持

⭐⭐ 教育和教学

Lua 的简洁语法和相对容易的学习曲线使其成为教学编程概念的好工具尤其是对于初学者来说

⭐⭐ 脚本自动化

Lua 常用于自动化任务比如编写系统管理脚本或在服务器环境中执行特定任务

⭐⭐ 其他领域

Lua 在许多其他领域也有应用比如在音频处理视频编辑软件中作为扩展语言等

环境搭建

安装 Lua 解释器

Windows 平台

  1. 访问官网前往 Lua 官方网站 www.lua.org
  2. 下载 Lua点击downloads或者下载链接找到最新稳定版的 Lua如 Lua 5.4下载 Windows 版本的安装包
  3. 安装 Lua双击下载的安装包按照提示完成安装过程通常情况下你可以选择默认安装路径或者指定一个路径
  4. 验证安装打开命令提示符窗口输入 lua如果看到 Lua 的命令行提示符 =则表示安装成功

macOS/Linux 平台

  1. 下载源码同样从 Lua 官网下载源码包
  2. 解压源码包使用 tar 或者 unzip 工具解压下载的文件
  3. 编译安装进入解压后的目录然后运行 make 编译 Lua之后运行 make install 进行安装这可能需要管理员权限
  4. 验证安装打开终端输入 lua检查是否能够正常启动

设置开发环境

Visual Studio Code

下载并安装 VS Code

安装 Lua 扩展插件如 lua-vscode以获得更好的 Lua 支持包括语法高亮智能感知等功能

Notepad++

下载并安装 Notepad++

启用 Lua 语言支持可以通过安装插件或手动设置来启用 Lua 语法高亮

Sublime Text

下载并安装 Sublime Text

通过 Package Control 安装 Lua 插件来增强 Lua 开发体验

其他编辑器/IDE

如 IntelliJ IDEA, Eclipse, Vim, Emacs 等这些工具也都有相应的 Lua 支持插件或配置

配置环境变量

如果希望在任何位置都能直接运行 Lua 脚本可以在系统环境变量中加入 Lua 的安装路径

Windows

找到 Lua 的安装路径如 C:\Program Files\lua

在系统属性中设置环境变量将 Lua 的 bin 目录添加到 PATH 环境变量中

macOS/Linux

在用户的 .bashrc 或 .zshrc 文件中添加 export PATH=$PATH:/path/to/lua/bin然后运行 source .bashrc 或 source .zshrc 使设置生效

测试 Lua 环境

  1. 创建一个简单的 Lua 文件如 test.lua内容如下
    1
    print("Hello, World!")
  2. 在命令行或终端中运行 lua test.lua如果能看到输出 “Hello, World!”说明一切正常

基本语法

注释

单行注释

使用两个减号 – 来添加单行注释

1
-- 这是一个单行注释

多行注释

使用两个减号加上一个左方括号 –[[ 和对应的右方括号 ]] 来添加多行注释

1
2
3
4
--[[ 
这是多行注释的第一行
可以跨越多行
直到结束标记 ]]

数据类型

数字

1
2
3
local i = 42           -- 整数
local f = 3.14 -- 浮点数
local scientific = 1e4-- 科学计数法表示的浮点数

字符串

字符串是字符序列可以用单引号双引号或者 [[ ]] 来定义

1
2
3
local s1 = 'hello'    -- 单引号字符串
local s2 = "world" -- 双引号字符串
local s3 = [[hello world]] -- 方括号字符串适合包含特殊字符

布尔值

布尔值有两种true 和 false

1
2
local b1 = true
local b2 = false

Nil

nil 是一种特殊的值用来表示没有值的状态

1
local n = nil

Table

Table 是 Lua 中的主要数据结构可以看作是数组或哈希表字典Table 可以用 {} 来创建并且可以存储任意类型的数据

1
2
3
local t = {}            -- 创建空 table
local t2 = {1, 2, 3} -- 创建数组风格的 table
local t3 = {a=1, b=2} -- 创建哈希风格的 table

运算符

算术运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+ 加法
- 减法
* 乘法
/ 除法
^ 幂运算
% 取模

local a = 10
local b = 5
print(a + b) -- 输出 15
print(a - b) -- 输出 5
print(a * b) -- 输出 50
print(a / b) -- 输出 2
print(a ^ 2) -- 输出 100
print(a % b) -- 输出 0

关系运算符

1
2
3
4
5
6
7
8
9
10
11
12
== 等于
~= 不等于
< 小于
> 大于
<= 小于或等于
>= 大于或等于

local x = 10
local y = 5
print(x == y) -- 输出 false
print(x > y) -- 输出 true
print(x ~= y) -- 输出 true

逻辑运算符

1
2
3
4
5
6
7
8
9
and 逻辑与
or 逻辑或
not 逻辑非

local a = true
local b = false
print(a and b) -- 输出 false
print(a or b) -- 输出 true
print(not a) -- 输出 false

流程控制

条件语句

if 语句用于基于条件判断来决定是否执行一段代码可以使用 elseif 和 else 来提供额外的条件判断和默认情况

1
2
3
4
5
6
7
8
local x = 10
if x < 0 then
print("x is negative")
elseif x > 0 then
print("x is positive")
else
print("x is zero")
end

循环语句

💗💗 for 循环

Lua 支持两种形式的 for 循环数值循环和泛型循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 数值循环
for i = 1, 5 do
print(i)
end

-- 泛型循环
-- 适用于迭代表中的元素
local t = {10, 20, 30}
for index, value in ipairs(t) do
print(index, value)
end
-- pairs 可以用来迭代表中的所有键值对
for key, value in pairs(t) do
print(key, value)
end

💗💗 while 循环

while 循环会持续执行一段代码直到给定的条件不再为真

1
2
3
4
5
local count = 1
while count <= 5 do
print(count)
count = count + 1
end

💗💗 repeat/until 循环

repeat 循环至少会执行一次然后检查条件如果条件为假则继续执行循环如果为真则终止循环

1
2
3
4
5
local count = 1
repeat
print(count)
count = count + 1
until count > 5

函数

定义函数

在 Lua 中使用 function 关键字来定义一个函数函数可以带有参数并且可以有返回值

1
2
3
function greet(name)
print("Hello, " .. name .. "!")
end

调用函数

定义完函数后可以通过函数名后跟一对圆括号来调用它并传入必要的参数

1
greet("Alice")  -- 输出: Hello, Alice!

参数传递

值传递

在 Lua 中当你传递一个基本类型如数字字符串布尔值给函数时传递的是该值的拷贝因此在函数内部对该值所做的任何改变都不会影响到原始变量

1
2
3
4
5
6
7
function increment(num)
num = num + 1
end

local x = 5
increment(x)
print(x) -- 输出: 5

引用传递

当传递一个复合类型如 table 或者 userdata实际上是传递了一个指向该对象的引用因此在函数内部对该对象所做的任何修改都会影响到原始对象

1
2
3
4
5
6
7
function addOne(tbl)
tbl.value = tbl.value + 1
end

local t = {value = 5}
addOne(t)
print(t.value) -- 输出: 6

返回值

Lua 中的函数可以返回多个值返回多个值时只需在 return 后面列出值即可

1
2
3
4
5
6
function divide(dividend, divisor)
return dividend / divisor, dividend % divisor
end

local quotient, remainder = divide(10, 3)
print(quotient, remainder) -- 输出: 3.3333333333333 1

匿名函数

匿名函数是在定义时不赋予名称的函数可以立即定义并调用

1
2
3
4
local result = (function(x)
return x * x
end)(5)
print(result) -- 输出: 25

闭包

闭包是指一个函数可以访问在其外部定义的作用域中的变量这种能力使得函数可以记住并访问其定义时的上下文

1
2
3
4
5
6
7
8
function makeAdder(n)
return function(x)
return x + n
end
end

local addFive = makeAdder(5)
print(addFive(10)) -- 输出: 15

泛型函数

尽管 Lua 本身不支持真正的泛型编程如 C++ 或 Java 中的模板/泛型但我们可以通过一些编程技巧来实现类似的功能泛型编程的核心思想是在不指定具体类型的情况下编写代码从而可以用于多种类型

1
2
3
4
5
6
7
8
9
10
11
12
13
local function printValue(value)
if type(value) == "number" then
print("Number:", value)
elseif type(value) == "string" then
print("String:", value)
else
print("Unknown type:", value)
end
end

printValue(42) -- 输出: Number: 42
printValue("Hello") -- 输出: String: Hello
printValue({a=1, b=2}) -- 输出: Unknown type: table

泛型容器

可以定义一个泛型容器该容器可以存储任何类型的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local function createContainer()
local container = {}

function container.setValue(value)
container.value = value
end

function container.getValue()
return container.value
end

return container
end

local c = createContainer()
c:setValue("Hello")
print(c:getValue()) -- 输出: Hello

错误处理

assert 函数

assert 函数用于断言某个条件为真如果条件不满足则抛出错误

1
2
3
local x = tonumber(io.read())  -- 读取一行输入并尝试转换为数字
assert(x ~= nil, "Not a number") -- 如果 x 为 nil则抛出错误
print(x)

pcall 函数

pcall保护调用允许你在发生错误时捕获异常而不中断程序执行

1
2
3
4
5
6
7
local success, result = pcall(function()
error("This is an error message", 2)
end)

if not success then
print(result)
end

xpcall 函数

xpcall 与 pcall 类似但是它允许你在错误发生时传递一个错误处理器函数

1
2
3
4
5
6
7
8
9
10
11
local errHandler = function(err)
print("Caught error:", err)
end

local success, result = xpcall(function()
error("This is an error message", 2)
end, errHandler)

if not success then
print(result)
end

table

Lua 中的表table是一种非常强大的数据结构可以被用作数组关联数组堆栈队列集合映射等

创建空表

1
local t = {}

初始化表

1
2
3
4
5
-- 或者使用数字索引来创建数组样式的表
local t = {"value1", "value2", "value3"}

-- 可以在创建表的同时初始化它使用键值对的形式来填充表
local t = {key1 = "value1", key2 = "value2"}

访问表元素

可以通过索引来访问表中的元素下标从 1 开始

1
2
local t = {10, 20, 30}
print(t[1]) -- 输出: 10

修改表元素

直接通过索引赋值即可修改表中的元素

1
2
t[1] = 5
print(t[1]) -- 输出: 5

表的遍历

使用 ipairs 遍历

ipairs 函数适用于遍历数组样式的表即那些索引为连续正整数的表

1
2
3
4
5
6
7
8
9
local t = {"value1", "value2", "value3"}

for i, v in ipairs(t) do
print(i, v)
end
-- 输出:
-- 1 value1
-- 2 value2
-- 3 value3

使用 pairs 遍历

pairs 函数返回一个迭代器可以用来遍历表中的所有键值对

1
2
3
4
5
6
7
8
local t = {key1 = "value1", key2 = "value2"}

for k, v in pairs(t) do
print(k, v)
end
-- 输出:
-- key1 value1
-- key2 value2

使用 next 遍历

next 函数返回表中的下一个键值对通常配合循环使用来遍历整个表

1
2
3
4
5
6
7
8
local t = {key1 = "value1", key2 = "value2"}
local k, v = next(t)

while k do
print(k, v)
k, v = next(t, k)
end
-- 输出同 `pairs` 示例

元表

元表是另一个表它定义了如何处理表的一些操作可以为一个表设置元表来改变其行为

1
2
3
4
local t = {key1 = "value1"}
local mt = {__index = _G} -- 使用全局环境作为查找表

setmetatable(t, mt)

魔术方法

魔术方法是用来定义表的行为的特殊键值通过设置元表和使用魔术方法可以定制表的行为从而实现更复杂的数据结构和功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- __index当访问的键不存在时Lua 会使用这个方法来查找值
-- __newindex当尝试设置一个不存在的键时Lua 会使用这个方法来设置值
-- __metatable尝试获取表的元表时调用
-- __tostring转换表为字符串时调用
-- __len获取表长度时调用
-- __call当表被当作函数调用时调用

local t = {}
local mt = {
__index = function(t, k)
return k .. " has no value"
end,
__newindex = function(t, k, v)
print("Setting " .. k .. " to " .. v)
end,
__tostring = function(t)
return "Custom tostring implementation"
end
}

setmetatable(t, mt)

print(t.key) -- 输出: key has no value
t.key = "value" -- 输出: Setting key to value
print(tostring(t)) -- 输出: Custom tostring implementation

面向对象

概念

在 Lua 中面向对象编程OOP不是内置的语言特性而是通过 Lua 的元表机制和表的特性出来的一个本质上就是一个表包含了类的方法和属性对象则是这个类的一个实例也是一个表通常会包含类的属性

在 Lua 中我们可以通过函数来创建这个函数通常会返回一个表这个表可以看作是类的原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 构造函数
function MyClass()
local self = setmetatable({}, {__index = MyClass}) -- 创建一个新的对象并设置元表
self.value = 0 -- 设置对象的属性
return self
end

-- 创建类中的方法
MyClass.method = function(self)
print("Method called with value:", self.value)
end

-- 创建对象
local obj = MyClass()
obj.method() -- 输出: Method called with value: 0

继承

在 Lua 中继承也是通过表的元表机制实现的子类可以继承父类的属性和方法

通过设置子类的元表为父类子类就可以访问父类的方法和属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function MyBaseClass()
local self = setmetatable({}, {__index = MyBaseClass})
self.value = 0
return self
end

MyBaseClass.method = function(self)
print("Base method called with value:", self.value)
end

function MyDerivedClass()
local self = setmetatable({}, {__index = MyBaseClass}) -- 设置元表为父类
self.value = 10
return self
end

-- 创建子类对象
local derivedObj = MyDerivedClass()
derivedObj.method() -- 输出: Base method called with value: 10

多态

多态是指子类可以覆盖父类的方法表现出不同的行为

1
2
3
4
5
MyDerivedClass.method = function(self)
print("Derived method called with value:", self.value)
end

derivedObj.method() -- 输出: Derived method called with value: 10

接口

Lua 中并没有原生的接口interface概念但是可以通过约定convention和规范specification来达到类似的效果例如定义一组必须的方法并确保每个对象实现了这些方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 假设这是一个接口
local IShape = {
draw = function(self) end
}

-- 实现接口的一个对象
local Rectangle = {}
setmetatable(Rectangle, {__index = IShape})

Rectangle.draw = function(self)
print("Drawing a rectangle")
end

local rect = setmetatable({}, {__index = Rectangle})
rect:draw() -- 输出: Drawing a rectangle

委托

Lua 中没有直接的委托delegate概念但可以使用函数作为值来传递和存储从而实现类似的功能委托通常用于传递函数作为参数或者作为对象的回调方法

1
2
3
4
5
6
7
8
9
local function myDelegate(callback)
callback()
end

local function myCallback()
print("Callback was called")
end

myDelegate(myCallback) -- 输出: Callback was called

协程

协程是一种比线程更轻量级的并发模型一个协程可以暂停执行并在适当的时候恢复执行

1
2
3
4
5
6
7
8
9
local function coExample()
print("Coroutine started")
coroutine.yield() -- 暂停协程
print("Coroutine resumed")
end

local co = coroutine.create(coExample)
coroutine.resume(co) -- 启动协程
coroutine.resume(co) -- 恢复协程

模块化

在 Lua 中可以通过模块来组织代码使得代码更加模块化和易于维护一个模块通常是一个单独的文件它导出一系列的函数或变量

创建模块

模块可以是一个简单的文件里面包含了一系列的函数定义

1
2
3
4
5
6
7
8
-- 本文件名mymodule.lua
local m = {}

function m.greet(name)
print("Hello, " .. name .. "!")
end

return m

导入模块

在其他文件中可以通过 require 函数来导入模块

1
2
3
4
-- 本文件名main.lua
local mod = require('mymodule') -- 导入模块

mod.greet("Alice") -- 输出: Hello, Alice!

热更新

热更新通常涉及到模块的重新加载在 Lua 中可以通过重新定义模块来实现热更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 本文件名mymodule.lua
local mod = {}
mod.someFunction = function()
print("someFunction called")
end
return mod

-- 主程序
local mod = require('mymodule')
mod:someFunction() -- 输出: someFunction called

-- 重新定义模块
package.loaded['mymodule'] = nil
local newMod = require('mymodule')
newMod:someFunction() -- 输出: someFunction called

I/O操作

打开文件

在 Lua 中可以使用 io.open 函数来打开一个文件这个函数接受两个参数文件名和打开模式

1
2
3
4
5
6
7
8
9
10
--[[
打开模式可以是
'r'只读模式默认为从文件开头开始读取
'w'写入模式默认为从文件开头开始写入如果文件已存在则会被截断为空文件
'a'追加模式所有写入操作都会被添加到文件末尾
'b'二进制模式
'+'读写模式
]]

local file = io.open("example.txt", "w")

关闭文件

关闭文件是很重要的因为它释放了文件资源并确保所有的缓冲数据都被写入磁盘

1
file:close()

写入文件

1
2
3
local file = io.open("example.txt", "w")
file:write("Hello, World!\n")
file:close()

读取文件

1
2
3
4
5
6
7
8
9
10
11
12
--[[
*n读取一个数字
*l读取一行字符串直到遇到换行符
*a读取所有剩余内容
*c读取一个字符
*u读取一个短字符串Unicode 字符
]]

local file = io.open("example.txt", "r")
local line = file:read("*l") -- 读取一行
print(line) -- 输出: Hello, World!
file:close()

格式化输出

1
2
3
4
5
6
local number = 123
local formatted_string = string.format("%d is the magic number\n", number)

local file = io.open("example.txt", "w")
file:write(formatted_string)
file:close()

标准输入输出

1
2
3
4
5
6
7
8
9
-- 从标准输入读取一行
local input = io.stdin:read("*l")
print("You entered:", input)

-- 向标准输出写入一行
io.stdout:write("Output from program\n")

-- 向标准错误输出写入一行
io.stderr:write("Error message\n")

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 打开一个文件用于写入
local file = io.open("example.txt", "w")

-- 写入一些数据
file:write("Hello, World!\n")
file:write(string.format("The answer is %d\n", 42))

-- 关闭文件
file:close()

-- 重新打开文件用于读取
file = io.open("example.txt", "r")

-- 读取文件内容
local content = file:read("*a")

-- 打印文件内容
print(content)

-- 关闭文件
file:close()

标准库

Lua 的标准库包含了一系列预定义的模块这些模块提供了丰富的功能可以帮助开发者处理各种常见的编程任务

_G 表

_G 是一个全局环境表所有的全局变量都是 _G 表中的键值对这意味着你可以在 _G 表中直接访问或修改全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- 创建全局变量
_G.x = 10
print(_G.x) -- 输出: 10

-- 访问全局变量
y = 20
print(_G.y) -- 输出: 20
print(_G["y"]) -- 输出: 20

-- 修改全局变量
_G.x = 15
print(x) -- 输出: 15

-- 删除全局变量
_G.x = nil
print(x) -- 输出: nil

-- 访问内置函数和常量
print(_G.math) -- 输出: table: 0x7fffffffe7e0
print(_G.os) -- 输出: table: 0x7fffffffe750
print(_G.print) -- 输出: function: 0x7fffffffec40
print(_G.string) -- 输出: table: 0x7fffffffe7b0
print(_G.table) -- 输出: table: 0x7fffffffe7c0

-- 使用 _G 表来定义和调用函数
_G.myFunc = function()
print("Called myFunc")
end
myFunc() -- 输出: Called myFunc

-- 检查 _G 表的内容
print("Global variables:")
for k, v in pairs(_G) do
print(k, v)
end

package 模块

package 模块是 Lua 中用于管理模块加载路径和模块搜索策略的核心模块通过 package 模块可以控制 Lua 如何查找和加载模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
-- 输出默认的配置字符串
print(package.config)

-- 输出默认的路径列表
print(package.path)

-- 修改路径列表添加自定义路径
package.path = package.path .. ";./?.lua;./lib/?.lua;./lib/?/init.lua"

-- 输出修改后的路径列表
print(package.path)

-- 输出默认的 C 语言模块路径列表
print(package.cpath)

-- 修改 C 语言模块路径列表添加自定义路径
package.cpath = package.cpath .. ";./?.so"

-- 输出修改后的 C 语言模块路径列表
print(package.cpath)

-- 输出搜索函数列表
for i, searcher in ipairs(package.searchers) do
print("Searcher", i, ":", searcher)
end

-- 输出加载函数列表
for i, loader in ipairs(package.loaders) do
print("Loader", i, ":", loader)
end

-- 添加一个模块的加载函数到预加载表
package.preload.myModule = function()
return {name="My Module"}
end

-- 加载模块
local mod = require('myModule')
print(mod.name) -- 输出: My Module

-- 设置全局环境表的元表
package.seeall(_G)

-- 加载模块
mod = require('myModule')
print(mod.name) -- 输出: My Module

-- 查找模块的实际路径
local path = package.searchpath('myModule', package.path)
print(path) -- 输出模块的实际路径

-- 加载 C 语言编写的共享库
local dlopen = package.loadlib("libm.so", "luaopen_m")
dlopen() -- 加载 C 库并注册到 Lua 中
print(math.pi) -- 输出: 3.141592653589793

coroutine 模块

coroutine 模块是 Lua 中用于支持协同程序coroutine的功能模块协同程序是一种轻量级的线程可以在程序的不同部分之间切换执行通过 coroutine 模块可以在 Lua 中创建控制和管理协同程序

coroutine.create

创建一个新的协同程序func 是一个 Lua 函数该函数将在新创建的协同程序中执行

1
2
3
4
5
6
7
8
local co = coroutine.create(function()
print("Coroutine started")
coroutine.yield() -- 暂停执行
print("Coroutine resumed")
end)

coroutine.resume(co) -- 启动协同程序
coroutine.resume(co) -- 恢复协同程序

coroutine.resume

恢复一个暂停的协同程序 co 的执行可以传递额外的参数给协同程序的 yield 方法

1
2
3
4
5
6
7
local co = coroutine.create(function()
local result, arg1, arg2 = coroutine.yield() -- 暂停并接收参数
print("Received:", result, arg1, arg2)
end)

coroutine.resume(co) -- 启动协同程序
coroutine.resume(co, "result", "arg1", "arg2") -- 恢复协同程序并传递参数

coroutine.running

返回当前正在运行的协同程序的句柄如果是主协同程序启动程序则返回 nil

1
2
3
4
5
6
7
local co = coroutine.create(function()
print("Running coroutine:", coroutine.running())
coroutine.yield()
end)

coroutine.resume(co) -- 启动协同程序
print("Running main coroutine:", coroutine.running()) -- 输出: Running main coroutine: nil

coroutine.status

返回一个字符串指示协同程序 co 的状态可能的状态包括 “running”“normal”“suspended”“dead”

1
2
3
4
5
6
7
8
9
10
11
local co = coroutine.create(function()
print("Running coroutine:", coroutine.status(co))
coroutine.yield()
print("Resumed coroutine:", coroutine.status(co))
end)

print("Initial status:", coroutine.status(co)) -- 输出: Initial status: normal
coroutine.resume(co) -- 启动协同程序
print("Suspended status:", coroutine.status(co)) -- 输出: Suspended status: suspended
coroutine.resume(co) -- 恢复协同程序
print("Final status:", coroutine.status(co)) -- 输出: Final status: normal

coroutine.wrap

返回一个新函数该函数每次调用时都会创建一个新的协同程序并执行 func这可以用来创建可重用的协同程序

1
2
3
4
5
6
7
8
local wrappedFunc = coroutine.wrap(function()
print("Wrapped coroutine started")
coroutine.yield()
print("Wrapped coroutine resumed")
end)

wrappedFunc() -- 启动协同程序
wrappedFunc() -- 再次启动新的协同程序

coroutine.yield

暂停当前协同程序的执行并返回可选的参数这是协同程序之间传递控制的主要手段

1
2
3
4
5
6
7
8
9
local co = coroutine.create(function()
print("Before yield")
coroutine.yield("Result from coroutine")
print("After yield")
end)

coroutine.resume(co) -- 启动协同程序
local status, result = coroutine.resume(co) -- 恢复协同程序并获取结果
print("Result:", result) -- 输出: Result: Result from coroutine

示例代码

💗💗 使用 coroutine 模块来创建控制和管理协同程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
-- 创建一个协同程序
local co = coroutine.create(function()
print("Coroutine started")
coroutine.yield() -- 暂停执行
print("Coroutine resumed")
end)

-- 启动协同程序
coroutine.resume(co)

-- 恢复协同程序
coroutine.resume(co)

-- 获取当前正在运行的协同程序
print("Running coroutine:", coroutine.running()) -- 输出: Running coroutine: nil

-- 创建另一个协同程序并使用 wrap 包装
local wrappedFunc = coroutine.wrap(function()
print("Wrapped coroutine started")
coroutine.yield()
print("Wrapped coroutine resumed")
end)

-- 调用包装后的函数
wrappedFunc()
wrappedFunc()

-- 获取协同程序的状态
local co2 = coroutine.create(function()
print("Running coroutine:", coroutine.status(co2))
coroutine.yield()
print("Resumed coroutine:", coroutine.status(co2))
end)

-- 启动并获取状态
coroutine.resume(co2)
print("Suspended status:", coroutine.status(co2)) -- 输出: Suspended status: suspended
coroutine.resume(co2)
print("Final status:", coroutine.status(co2)) -- 输出: Final status: normal

debug 模块

debug 模块是 Lua 中用于调试和追踪程序执行过程的重要工具它提供了多种方法来获取运行时的信息设置调试钩子调整环境等

debug.getinfo

1
2
3
4
5
6
7
8
-- debug.getinfo(f, level[, what])
-- 获取函数 f 在层级 level 上的信息what 参数是一个字符串指定了想要获取的信息类型
function testInfo()
local info = debug.getinfo(1, 1, "Slnt")
print(info.short_src, info.currentline) -- 输出: 当前文件名和当前行号
end

testInfo()

debug.getregistry

1
2
3
4
-- debug.getregistry()
-- 获取 Lua 的全局注册表一个内部表用于存储元表等
local registry = debug.getregistry()
print(registry) -- 输出: table: 0x7fffffffef80

debug.setfenv

1
2
3
4
5
6
7
8
9
-- debug.setfenv(f, level, env)
-- 设置函数 f 在层级 level 上的环境表为 env
function testSetEnv()
local env = {}
debug.setfenv(1, 1, env) -- 设置当前函数的环境表
print(env) -- 输出: table: 0x7fffffffef80
end

testSetEnv()

debug.getfenv

1
2
3
4
5
6
7
8
-- debug.getfenv(f, level)
-- 获取函数 f 在层级 level 上的环境表
function testEnv()
local env = debug.getfenv(1, 1) -- 获取当前函数的环境表
print(env) -- 输出: table: 0x7fffffffef80
end

testEnv()

debug.sethook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- debug.sethook(f, mode, count)
-- 设置函数 f 的调试钩子mode 指定钩子的类型count 指定触发间隔
function testSetHook()
local count = 0
local hookFunc = function()
count = count + 1
print("Line", count)
end
debug.sethook(1, "l", 0, hookFunc) -- 设置每行执行时触发的钩子
for i = 1, 5 do
print(i) -- 输出: Line 1, 2, 3, 4, 5
end
end

testSetHook()

debug.gethook

1
2
3
4
5
6
7
8
-- debug.gethook(f, level)
-- 获取函数 f 在层级 level 上的调试钩子
function testHook()
local hook = debug.gethook(1, 1) -- 获取当前函数的调试钩子
print(hook) -- 输出: nil如果没有设置钩子的话
end

testHook()

debug.setlocal

1
2
3
4
5
6
7
8
9
-- debug.setlocal(f, level, name, value)
-- 设置函数 f 在层级 level 上的局部变量 name 的值为 value
function testSetLocal()
local a = 1
debug.setlocal(1, 1, "a", 10) -- 设置局部变量 a 的值为 10
print(a) -- 输出: 10
end

testSetLocal()

debug.getlocal

1
2
3
4
5
6
7
8
9
10
-- debug.getlocal(f, level, n)
-- 获取函数 f 在层级 level 上的局部变量名及其值
function testLocal()
local a = 1
local b = 2
local name, value = debug.getlocal(1, 1, 1) -- 获取第一个局部变量的名字和值
print(name, value) -- 输出: a 1
end

testLocal()

debug.setmetatable

1
2
3
4
5
6
-- debug.setmetatable(o, mt)
-- 设置对象 o 的元表为 mt
local t = {}
local mt = {}
debug.setmetatable(t, mt)
print(debug.getmetatable(t)) -- 输出: table: 0x7fffffffef80

debug.getmetatable

1
2
3
4
5
6
-- debug.getmetatable(o)
-- 获取对象 o 的元表
local t = {}
local mt = {}
setmetatable(t, mt)
print(debug.getmetatable(t)) -- 输出: table: 0x7fffffffef80

debug.setupvalue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- debug.setupvalue(f, n, v)
-- 设置闭包 f 的第 n 个上值为 v
local function outer()
local x = 10
local inner = function()
return x
end
return inner
end

local innerFunc = outer()
local _, _ = debug.getupvalue(innerFunc, 1)
debug.setupvalue(innerFunc, 1, 20)
print(innerFunc()) -- 输出: 20

debug.getupvalue

1
2
3
4
5
6
7
8
9
10
11
12
13
-- debug.getupvalue(f, n)
-- 获取闭包 f 的第 n 个上值的名字和值
local function outer()
local x = 10
local inner = function()
return x
end
return inner
end

local innerFunc = outer()
local name, value = debug.getupvalue(innerFunc, 1)
print(name, value) -- 输出: x 10

示例代码

💗💗 使用 debug 模块中的方法来获取和设置运行时的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
-- 获取函数信息
function testInfo()
local info = debug.getinfo(1, 1, "Slnt")
print(info.short_src, info.currentline) -- 输出: 当前文件名和当前行号
end

testInfo()

-- 获取局部变量
function testLocal()
local a = 1
local b = 2
local name, value = debug.getlocal(1, 1, 1) -- 获取第一个局部变量的名字和值
print(name, value) -- 输出: a 1
end

testLocal()

-- 获取元表
local t = {}
local mt = {}
setmetatable(t, mt)
print(debug.getmetatable(t)) -- 输出: table: 0x7fffffffef80

-- 获取注册表
local registry = debug.getregistry()
print(registry) -- 输出: table: 0x7fffffffef80

-- 获取上值
local function outer()
local x = 10
local inner = function()
return x
end
return inner
end

local innerFunc = outer()
local name, value = debug.getupvalue(innerFunc, 1)
print(name, value) -- 输出: x 10

-- 设置环境表
function testSetEnv()
local env = {}
debug.setfenv(1, 1, env) -- 设置当前函数的环境表
print(env) -- 输出: table: 0x7fffffffef80
end

testSetEnv()

-- 设置调试钩子
function testSetHook()
local count = 0
local hookFunc = function()
count = count + 1
print("Line", count)
end
debug.sethook(1, "l", 0, hookFunc) -- 设置每行执行时触发的钩子
for i = 1, 5 do
print(i) -- 输出: Line 1, 2, 3, 4, 5
end
end

testSetHook()

-- 设置局部变量
function testSetLocal()
local a = 1
debug.setlocal(1, 1, "a", 10) -- 设置局部变量 a 的值为 10
print(a) -- 输出: 10
end

testSetLocal()

-- 设置元表
local t = {}
local mt = {}
debug.setmetatable(t, mt)
print(debug.getmetatable(t)) -- 输出: table: 0x7fffffffef80

-- 设置上值
local function outer()
local x = 10
local inner = function()
return x
end
return inner
end

local innerFunc = outer()
local _, _ = debug.getupvalue(innerFunc, 1)
debug.setupvalue(innerFunc, 1, 20)
print(innerFunc()) -- 输出: 20

math 模块

math 模块是 Lua 中用于执行数学运算的标准库它提供了各种数学函数包括基本的数学运算指数和对数运算三角函数以及一些常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
-- 绝对值
print(math.abs(-3)) -- 输出: 3

-- 反余弦
print(math.acos(0.5)) -- 输出: 1.0471975511966 π/3

-- 反正弦
print(math.asin(0.5)) -- 输出: 0.5235987755983 π/6

-- 反正切
print(math.atan(1)) -- 输出: 0.78539816339745 π/4

-- 正切
print(math.atan2(1, 1)) -- 输出: 0.78539816339745 π/4

-- 向上取整
print(math.ceil(3.14)) -- 输出: 4

-- 余弦
print(math.cos(math.pi / 2)) -- 输出: 6.1232339957368e-17 接近 0

-- 双曲余弦
print(math.cosh(1)) -- 输出: 1.5430806348152

-- 弧度转角度
print(math.deg(math.pi)) -- 输出: 180

-- 自然指数
print(math.exp(1)) -- 输出: 2.718281828459 近似 e

-- 向下取整
print(math.floor(3.99)) -- 输出: 3

-- 取模
print(math.fmod(10, 3)) -- 输出: 1

-- 分解浮点数
local mantissa, exponent = math.frexp(123.45)
print(mantissa, exponent) -- 输出: 0.7715625 7

-- 重建浮点数
local result = math.ldexp(0.7715625, 7)
print(result) -- 输出: 123.45

-- 自然对数
print(math.log(math.exp(1))) -- 输出: 1

-- 以 10 为底的对数
print(math.log10(100)) -- 输出: 2

-- 最大值
print(math.max(1, 2, 3, 4, 5)) -- 输出: 5

-- 最小值
print(math.min(1, 2, 3, 4, 5)) -- 输出: 1

-- 分离整数和小数部分
local intPart, fracPart = math.modf(3.14)
print(intPart, fracPart) -- 输出: 3 0.14

-- 幂运算
print(math.pow(2, 3)) -- 输出: 8

-- 角度转弧度
print(math.rad(180)) -- 输出: 3.1415926535898

-- 随机数
print(math.random()) -- 输出: 0.42834488014292 0 到 1 之间的随机数
print(math.random(10)) -- 输出: 4 1 到 10 之间的随机整数
print(math.random(1, 100)) -- 输出: 57 1 到 100 之间的随机整数

-- 设置随机数种子
math.randomseed(os.time())
print(math.random()) -- 输出: 0.42834488014292 0 到 1 之间的随机数

-- 正弦
print(math.sin(math.pi / 2)) -- 输出: 1

-- 双曲正弦
print(math.sinh(1)) -- 输出: 1.1752011936438

-- 平方根
print(math.sqrt(16)) -- 输出: 4

-- 正切
print(math.tan(math.pi / 4)) -- 输出: 1

-- 双曲正切
print(math.tanh(1)) -- 输出: 0.76159415595576

-- 常量 huge
print(math.huge) -- 输出: inf

-- 常量 pi
print(math.pi) -- 输出: 3.1415926535898

string 模块

string 模块是 Lua 中用于处理字符串的标准库它提供了多种方法来创建比较分割拼接字符串以及其他常用的字符串操作功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
-- 返回字符串 s 的长度字节数
local str = "Hello, World!"
print(string.len(str)) -- 输出: 13

-- 返回字符串 s 中位置 i 开始的 n 个字符的字节值默认情况下返回单个字符的字节值
local str = "Hello, World!"
print(string.byte(str, 1)) -- 输出: 72
print(string.byte(str, 7, 8)) -- 输出: 87 111

-- 接受一系列数字并返回一个由这些数字对应的 ASCII 字符组成的字符串
local str = string.char(72, 101, 108, 108, 111)
print(str) -- 输出: Hello

-- 重复字符串 s n 次
local str = "abc"
print(string.rep(str, 3)) -- 输出: abcabcabc

-- 查找模式 pattern 在字符串 s 中的位置返回模式匹配的首尾索引如果未找到返回 nil
local str = "Hello, World!"
local start, endPos = string.find(str, "World")
print(start, endPos) -- 输出: 7 11

-- 返回一个迭代器用于逐个匹配字符串 s 中的模式 pattern
local str = "100 apples and 20 oranges"
for num in string.gmatch(str, "%d+") do
print(num)
end -- 输出: 100 20

-- 替换字符串 s 中的模式 pattern 为 repln 指定替换的最大次数
local str = "Hello, World!"
local newStr = string.gsub(str, "o", "O", 2)
print(newStr) -- 输出: HellO, WOrld!

-- 返回字符串 s 从位置 i 开始到位置 j 结束的子串如果省略 j则返回从 i 到字符串末尾的子串
local str = "Hello, World!"
print(string.sub(str, 8)) -- 输出: World!

-- 返回字符串 s 的逆序形式
local str = "Hello, World!"
print(string.reverse(str)) -- 输出: !dlroW ,olleH

-- 返回字符串 s 的小写形式
local str = "HELLO, WORLD!"
print(string.lower(str)) -- 输出: hello, world!

-- 返回字符串 s 的大写形式
local str = "hello, world!"
print(string.upper(str)) -- 输出: HELLO, WORLD!

-- 格式化按照 format 格式化字符串并返回格式化后的字符串
local number = 10
print(string.format("The number is %d", number)) -- 输出: The number is 10

-- 打包接受一系列值和格式字符串 fmt并返回一个包含这些值的二进制表示的字符串
local packed = string.pack("ii", 1, 2)
print(packed) -- 输出: 字符串形式的二进制数据

-- 打包大小接受一系列值和格式字符串 fmt并返回需要的字节数来表示这些值
local size = string.packsize("ii", 1, 2)
print(size) -- 输出: 需要的字节数

-- 解包接受格式字符串 fmt 和二进制字符串 s并返回按照 fmt 解析得到的一系列值
local packed = string.pack("ii", 1, 2)
local a, b = string.unpack("ii", packed)
print(a, b) -- 输出: 1 2

table 模块

table 模块是 Lua 中用于处理表格tables的标准库表格是 Lua 中的一种数据结构它可以看作是关联数组能够存储任意类型的值并且可以用任意类型的值作为键table 模块提供了多种方法来创建排序遍历表格以及其他常用的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
-- 连接表格元素
local t = {"Hello", "World", "!"}
print(table.concat(t, ", ")) -- 输出: Hello, World, !

-- 遍历表格元素
local t = {a=1, b=2, c=3}
table.foreach(t, function(k, v) print(k, v) end)
-- 输出:
-- a 1
-- b 2
-- c 3

-- 遍历表格元素并提供索引
local t = {"a", "b", "c"}
table.foreachi(t, function(i, k, v) print(i, v) end)
-- 输出:
-- 1 a
-- 2 b
-- 3 c

-- 插入元素
local t = {1, 2, 4}
table.insert(t, 3) -- 在末尾插入 3
print(table.concat(t, ", ")) -- 输出: 1, 2, 4, 3
table.insert(t, 2, "new") -- 在索引 2 处插入 "new"
print(table.concat(t, ", ")) -- 输出: 1, new, 2, 4, 3

-- 获取最大的索引值
local t = {1, 2, 3}
print(table.maxn(t)) -- 输出: 3

-- 移除元素
local t = {1, 2, 3, 4}
table.remove(t) -- 移除最后一个元素
print(table.concat(t, ", ")) -- 输出: 1, 2, 3
table.remove(t, 2) -- 移除索引为 2 的元素
print(table.concat(t, ", ")) -- 输出: 1, 3

-- 排序表格
local t = {3, 1, 2}
table.sort(t)
print(table.concat(t, ", ")) -- 输出: 1, 2, 3

local t = {{"b", 2}, {"a", 1}}
table.sort(t, function(a, b) return a[1] < b[1] end)
print(table.concat(table.move(t, 1, #t, 1, {}), ", ")) -- 输出: a, 1, b, 2

os 模块

os 模块是 Lua 中用于与操作系统交互的标准库它提供了各种与系统时间环境变量文件执行等相关的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-- 获取当前时间
local now = os.time()
print(now) -- 输出当前 Unix 时间戳

-- 获取当前时间的格式化字符串
local dateString = os.date("%Y-%m-%d %H:%M:%S", now)
print(dateString) -- 输出: 当前日期和时间

-- 获取两个时间戳之间的差值
local t1 = os.time()
local t2 = os.time()
local diff = os.difftime(t2, t1)
print("Difference: " .. diff .. " seconds") -- 输出: Difference: 0 seconds

-- 执行一个操作系统命令
os.execute("echo Hello, World!") -- 在命令行环境下输出 Hello, World!

-- 终止程序
-- os.exit(0, true) -- 终止程序

-- 获取环境变量
local path = os.getenv("PATH")
print(path) -- 输出环境变量 PATH 的值

-- 设置本地化设置
local currentLocale = os.setlocale() -- 获取当前本地化设置
print(currentLocale) -- 输出当前本地化设置
os.setlocale("C") -- 设置为 C 语言环境

-- 获取程序运行时间
local startTime = os.clock()
local endTime = os.clock()
local elapsed = endTime - startTime
print("Elapsed time: " .. elapsed .. " seconds") -- 输出: Elapsed time: 0.0001 seconds

io 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
-- 打开文件
local file = io.open("example.txt", "w")
file:write("Hello, World!\n")
file:close()

-- 重新打开文件以读取内容
file = io.open("example.txt", "r")
local content = file:read("*a")
print(content) -- 输出: Hello, World!

-- 使用 io.lines 逐行读取文件
for line in io.lines("example.txt") do
print(line)
end

-- 使用 io.popen 执行命令
local cmdOutput = io.popen("ls", "r")
for line in cmdOutput:lines() do
print(line)
end
cmdOutput:close()

-- 创建临时文件
local tempFile = io.tmpfile()
tempFile:write("Temporary data\n")
tempFile:close()

-- 读取标准输入
print(io.read()) -- 读取一行

-- 读取整数
print(io.read("*n")) -- 读取一个整数

-- 读取所有剩余内容
print(io.read("*a")) -- 读取所有剩余内容

-- 写入标准输出
io.write("Hello, ", "World!", "\n")

-- 设置标准输入
io.input(io.open("input.txt", "r"))
local inputLine = io.read()
print(inputLine)

-- 设置标准输出
io.output(io.open("output.txt", "w"))
print("Hello, World!")

-- 关闭文件
io.close(file)

-- 刷新标准输出
io.flush()

bit32 模块

bit32 模块是 Lua 5.2 及以上版本中用于位操作的标准库它提供了一系列用于处理整数位运算的功能如位移逻辑运算旋转等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- 逻辑右移
print(bit32.arshift(0b1100, 2)) -- 输出: 3 (即 0b0011)
print(bit32.arshift(0b1100, -2)) -- 输出: 12 (即 0b1100 << 2)

-- 按位与运算
print(bit32.band(0b1100, 0b1010)) -- 输出: 12 (即 0b1000)

-- 按位取反运算
print(bit32.bnot(0b1100)) -- 输出: -13 (即 0b0011 取反后加上符号扩展)

-- 按位或运算
print(bit32.bor(0b1100, 0b1010)) -- 输出: 14 (即 0b1110)

-- 测试某一位是否为 1
print(bit32.btest(0b1100, 2)) -- 输出: true
print(bit32.btest(0b1100, 1)) -- 输出: false

-- 按位异或运算
print(bit32.bxor(0b1100, 0b1010)) -- 输出: 6 (即 0b0110)

-- 提取某一段位
print(bit32.extract(0b11001010, 2, 3)) -- 输出: 3 (即 0b011)
print(bit32.extract(0b11001010, 2)) -- 输出: 2 (即 0b010)

-- 替换某一段位
print(bit32.replace(0b11001010, 0b101, 2, 3)) -- 输出: 202 (即 0b11001010 -> 0b11001101)
print(bit32.replace(0b11001010, 0b101, 2)) -- 输出: 202 (即 0b11001010 -> 0b11001101)

-- 循环左移
print(bit32.lrotate(0b1100, 2)) -- 输出: 12 (即 0b1100 << 2 -> 0b0011)
print(bit32.lrotate(0b1100, -2)) -- 输出: 3 (即 0b1100 >> 2 -> 0b0011)

-- 循环右移
print(bit32.rrotate(0b1100, 2)) -- 输出: 3 (即 0b1100 >> 2 -> 0b0011)
print(bit32.rrotate(0b1100, -2)) -- 输出: 12 (即 0b1100 << 2 -> 0b1100)

utf8 模块

utf8 模块是 Lua 中用于处理 UTF-8 编码字符串的标准库UTF-8 是一种可变长度的字符编码方式它可以用来表示 Unicode 字符集中的任何字符utf8 模块提供了一些函数来帮助开发者处理这种编码的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 根据 Unicode 码点生成字符串
local str = utf8.char(0x68, 0x65, 0x6c, 0x6c, 0x6f) -- 'h', 'e', 'l', 'l', 'o'
print(str) -- 输出: hello
local str2 = utf8.char(0x1f600) -- Unicode 笑脸
print(str2) -- 输出: 😀

-- 获取字符串中字符的 Unicode 码点
local str = "😀你好"
local codepoint = utf8.codepoint(str, 1)
print(codepoint) -- 输出: 128512 (😀的Unicode码点)

-- 获取字符串中字符的偏移量
local str = "😀你好"
local offset = utf8.offset(str, 1)
print(offset) -- 输出: 1 (第一个字符的偏移量)

-- 计算字符串中字符的数量
local str = "😀你好"
local length = utf8.len(str)
print(length) -- 输出: 3 (一个笑脸和两个汉字)

-- 获取字符串的子串
local str = "😀你好世界"
local subStr = utf8.sub(str, 2, 4)
print(subStr) -- 输出: 你好

LuaJIT

LuaJITLua Just-In-Time Compiler是一个高性能的Lua解释器它为Lua 5.1提供即时编译JIT功能LuaJIT不仅提高了Lua代码的执行效率而且还增加了许多有用的特性如FFIForeign Function Interface和更高效的内存管理

LuaJIT的特点

  1. 即时编译JITLuaJIT能够在运行时将热点代码hot path编译成本地机器码从而显著提高执行效率
  2. 内存管理LuaJIT改进了垃圾回收机制减少了内存碎片和内存开销
  3. FFI支持LuaJIT支持FFI可以直接调用C语言函数无需使用传统的Lua-C桥接
  4. 多线程支持LuaJIT支持多线程可以通过协程coroutine实现并发执行
  5. 向量化指令集LuaJIT支持SSE/SSE2/SSE3/AVX指令集可以利用现代CPU的SIMD单指令多数据能力来加速计算密集型任务

主要模块和功能

  • 外部函数接口ffi模块允许Lua代码直接操作C语言的数据类型和调用C语言的函数这使得Lua可以无缝集成C语言库无需编写额外的包装代码
  • JIT编译LuaJIT的JIT编译器会自动识别热点代码并将其编译成本地机器码这意味着对于频繁执行的代码片段LuaJIT会在运行时对其进行优化
  • 内存管理LuaJIT改进了垃圾回收机制减少了内存碎片和内存开销它使用了一种称为增量标记-清除的垃圾回收算法可以在代码执行的同时进行垃圾回收
  • 协程支持LuaJIT支持协程可以轻松实现非阻塞的并发执行
  • 向量化指令集LuaJIT支持SSE/SSE2/SSE3/AVX指令集可以利用现代CPU的SIMD能力来加速计算密集型任务

安装 LuaJIT

💗💗 在 Linux 上安装 LuaJIT

1
sudo apt-get install lua5.1 luajit

💗💗 在 macOS 上安装 LuaJIT

1
brew install luajit

💗💗 在 Windows 上安装 LuaJIT

1
可以从官网下载预编译的二进制包或者使用 Chocolatey 等包管理工具

LuaJIT-FFI

LuaJIT-FFI 是 LuaJITJust-In-Time Compiler for Lua的一个重要特性允许 Lua 脚本直接调用 C 语言编写的函数并支持在 Lua 中使用 C 数据类型ffiForeign Function Interface提供了与 C 语言交互的能力使得 Lua 可以更加高效地利用底层库的功能

定义 C 类型和函数

1
2
3
4
5
ffi.cdef[[
typedef unsigned long uint32_t;
void* malloc(size_t size);
void free(void* ptr);
]]

加载 C 库

1
local libc = ffi.load("c")

创建 C 类型的实例

1
2
3
local intArray = ffi.new("int[10]")
intArray[0] = 42
print(intArray[0]) -- 输出 42

将一个值转换为指定的 C 类型

1
2
local intPtr = ffi.cast("int*", 42)
print(intPtr[0]) -- 输出 42

将一个 C 字符串转换为 Lua 字符串

1
2
3
local cStr = ffi.new("char[]", "Hello, world!")
local luaStr = ffi.string(cStr)
print(luaStr) -- 输出 "Hello, world!"

设置一个垃圾收集器函数来释放 C 动态分配的内存

1
2
local cStr = ffi.new("char[100]", "Hello, world!")
ffi.gc(cStr, ffi.C.free)

调用 C 函数

1
2
local sum = ffi.C.my_addition_function(10, 20)
print(sum) -- 输出 30

获取 C 类型的元信息

1
2
local intType = ffi.typeof("int")
print(intType.size) -- 输出 int 类型的大小

获取结构体成员相对于结构体起始位置的偏移量

1
2
3
4
5
6
7
8
9
ffi.cdef[[
struct Person {
char name[20];
int age;
};
]]

local nameOffset = ffi.offsetof("struct Person", "name")
print(nameOffset) -- 输出成员 "name" 的偏移量

获取 C 类型的大小

1
2
local intSize = ffi.sizeof("int")
print(intSize) -- 输出 int 类型的大小

获取 C 类型的名字

1
2
local intTypeName = ffi.C.typename(ffi.typeof("int"))
print(intTypeName) -- 输出 "int"

示例代码

💗💗 使用 luajit-ffi 模块中的方法来进行 C 语言的函数调用和类型操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
ffi.cdef[[
typedef unsigned long uint32_t;

// 定义一个简单的 C 函数
uint32_t add(uint32_t x, uint32_t y);

// 定义一个结构体
struct Point {
int x;
int y;
};

// 定义一个 C 函数来操作结构体
void setPoint(struct Point* point, int x, int y);
]]

-- 加载 C 库
local libc = ffi.load("c")

-- 创建一个 C 函数的指针
local addFunc = ffi.C.add

-- 调用 C 函数
local sum = addFunc(10, 20)
print(sum) -- 输出 30

-- 创建一个结构体实例
local point = ffi.new("struct Point")

-- 设置结构体的成员
ffi.C.setPoint(point, 100, 200)

-- 访问结构体成员
print(point.x) -- 输出 100
print(point.y) -- 输出 200

-- 创建一个字符数组
local cStr = ffi.new("char[100]", "Hello, world!")

-- 将 C 字符串转换为 Lua 字符串
local luaStr = ffi.string(cStr)
print(luaStr) -- 输出 "Hello, world!"

-- 分配内存
local mem = libc.malloc(100)
ffi.gc(mem, libc.free)

-- 将内存指针转换为字符串
local str = ffi.string(mem, 100)
str = "New string"

-- 将字符串转换回内存指针
ffi.C.memcpy(mem, ffi.string(str), #str)

-- 创建一个新的整数数组
local intArray = ffi.new("int[10]")
intArray[0] = 42
print(intArray[0]) -- 输出 42

-- 获取类型的信息
local intType = ffi.typeof("int")
print(intType.size) -- 输出 int 类型的大小

-- 获取结构体成员的偏移量
local xOffset = ffi.offsetof("struct Point", "x")
print(xOffset) -- 输出成员 "x" 的偏移量

Lua沙箱

在开发过程中尤其是在处理不可信的第三方代码或者用户提交的脚本时创建一个安全的执行环境是非常重要的Lua 沙箱Sandboxing是一种技术它可以在一个受限的环境中执行Lua代码防止恶意代码对系统造成损害通过沙箱技术我们可以限制代码的执行范围防止其访问敏感资源如文件系统网络或其他系统资源

💗💗 创建Lua沙箱有几种常见的方法包括但不限于

  1. 限制全局环境通过修改全局环境来限制可用的API
  2. 使用独立的Lua状态每个沙箱使用一个独立的Lua状态以防止不同沙箱之间的相互干扰
  3. 使用LuaJIT的保护模式在LuaJIT中可以使用保护模式来限制某些操作

限制全局环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 创建一个干净的环境
local env = {}

-- 将必要的库导入到新环境中
setmetatable(env, { __index = _G })
env.math = math
env.table = table
env.string = string
env.error = error
env.assert = assert

-- 设置新环境为全局环境
setfenv(1, env)

-- 示例在一个受限制的环境中执行代码
local code = [[
print("Hello, sandbox!")
io.open("secret.txt", "w"):write("This should not be written!") -- 尝试写入文件
]]
load(code)()

使用独立的Lua状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local L = require 'lua'
local state = L.newstate()

L.pushcfunction(state, function ()
print("Hello, isolated sandbox!")
local f = L.Lloadbuffer(state, [[
print("This is a sandboxed environment.")
io.open("secret.txt", "w"):write("This should not be written!")
]])
if L.pcall(state, f, 0, -2) ~= 0 then
local msg = L.tostring(state, -1)
print("Error:", msg)
end
end)

L.call(state)
L.close(state)

使用LuaJIT的保护模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 启用保护模式
local protect = require 'jit'.protect

-- 保护模式设置
local protectedEnv = {
nojit = true, -- 禁用JIT编译
nogc = true, -- 禁用垃圾回收
nodebug = true -- 禁用调试
}

-- 创建一个受保护的环境
local env = setmetatable({}, { __index = _G })
env.math = math
env.table = table
env.string = string
env.error = error
env.assert = assert

-- 应用保护模式
protect(env, protectedEnv)

-- 示例在一个受保护的环境中执行代码
local code = [[
print("Hello, protected sandbox!")
io.open("secret.txt", "w"):write("This should not be written!") -- 尝试写入文件
]]
load(code, nil, "t", env)()

第三方库

安装第三方库

手动安装

对于一些简单的库可以通过手动下载源代码文件并将其放在你的项目目录中来使用

  1. 下载源代码从官方 GitHub 仓库或其他源下载源代码
  2. 放置文件将下载的文件放到项目的某个目录下例如 libs 文件夹
  3. 加载库在 Lua 脚本中使用 require 加载库
1
2
-- 假设下载了一个名为 example.lua 的库文件并将其放在项目的 libs 目录下
local example = require('libs.example') -- 加载库

使用 LuaRocks 安装

LuaRocks 类似于 Python 的 pip 或 Node.js 的 npm是一个用于安装 Lua 库的包管理工具

  1. 安装 LuaRocks
1
2
3
4
5
6
7
8
# Ubuntu/Debian
sudo apt-get install luarocks

# macOS (使用 Homebrew)
brew install luarocks

# Windows (使用 Chocolatey)
choco install luarocks
  1. 使用 LuaRocks 安装
1
2
# 安装 LuaSocket
luarocks install lua-socket
  1. 加载库
1
local socket = require('socket')

自动构建和安装

对于需要编译的库如 luaposix 或者某些 C/C++ 扩展库可能需要先编译源代码再安装

  1. 下载源代码从官方仓库下载源代码
  2. 配置编译根据库提供的 README 或 INSTALL 文件进行配置和编译
  3. 安装库使用 make install 或者库提供的安装脚本进行安装
1
2
3
4
git clone https://github.com/luaposix/luaposix.git
cd luaposix
make
sudo make install

容器化安装

如果你在一个容器化的环境中工作可以将 Lua 和所需的库打包到 Docker 镜像中

1
2
3
4
FROM ubuntu:latest
RUN apt-get update && apt-get install -y luarocks
RUN luarocks install lua-socket
CMD ["lua", "your_script.lua"]

LuaSocket 库

LuaSocket 是一个用于网络编程的库提供了处理 TCP/IP 和 UDP 协议的功能这个库使得在 Lua 中进行网络通信变得非常简单和直观

socket.select

检测一组套接字的状态以确定哪些套接字准备好进行读取写入或出现异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
local select = require('socket').select

local rset = {socket.tcp(), socket.tcp()}
local wset = {socket.tcp(), socket.tcp()}
local eset = {}

local readyRead, readyWrite, excepted = select(rset, wset, eset, 0)
if readyRead then
for _, sock in ipairs(readyRead) do
print(sock:receive("*a"))
end
end
if readyWrite then
for _, sock in ipairs(readyWrite) do
print(sock:send("data"))
end
end
if excepted then
for _, sock in ipairs(excepted) do
print("Exception on socket:", sock)
end
end

socket.bind

绑定一个地址和端口到一个新的套接字

1
2
3
4
5
local bind = require('socket').bind

local server = bind("0.0.0.0", 8080, "inet")
server:listen(5)
print("Server bound to port 8080.")

socket.connect

连接到指定的主机和端口

1
2
3
4
5
6
7
local connect = require('socket').connect

local client = connect("www.example.com", 80)
client:send("GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n")
local response = client:receive("*a")
print(response)
client:close()

socket.tcp

创建一个新的 TCP 套接字

1
2
3
4
5
6
7
8
local tcp = require('socket').tcp

local sock = tcp()
sock:connect("www.example.com", 80)
sock:send("GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n")
local response = sock:receive("*a")
print(response)
sock:close()

socket.udp

创建一个新的 UDP 套接字

1
2
3
4
5
6
7
8
9
10
11
local udp = require('socket').udp

local sock = udp()
sock:setoption("reuseaddr", "on")
sock:bind("*", 12345)
local addr = sock:getpeername()
print(addr.ip, addr.port)
sock:sendto("Hello, UDP!", "127.0.0.1", 12345)
local data, peer = sock:receivefrom(1024)
print(data, peer.ip, peer.port)
sock:close()

socket.listen

监听指定端口上的连接请求

1
2
3
4
5
6
7
8
9
10
local listen = require('socket').listen

local server = listen(8080)
local client, addr = server:accept()
print("Connected by", addr.ip, addr.port)
local request = client:receive("*a")
print(request)
client:send("HTTP/1.0 200 OK\r\n\r\nHello, World!")
client:close()
server:close()

socket.gethostname

获取本地主机名

1
2
3
local gethostname = require('socket').gethostname

print(gethostname())

socket.gethostbyname

获取主机的 IP 地址

1
2
3
local gethostbyname = require('socket').gethostbyname

print(gethostbyname("www.example.com"))

socket.gethostbyaddr

获取 IP 地址对应的主机名

1
2
3
local gethostbyaddr = require('socket').gethostbyaddr

print(gethostbyaddr("127.0.0.1"))

socket.getaddrinfo

获取一个或多个地址信息记录

1
2
3
4
5
6
local getaddrinfo = require('socket').getaddrinfo

local addresses = getaddrinfo("www.example.com", "http")
for _, info in ipairs(addresses) do
print(info.family, info.socktype, info.protocol, info.address, info.port)
end

socket.resolve

解析主机名和端口号

1
2
3
4
local resolve = require('socket').resolve

local ip, port = resolve("www.example.com", "http")
print(ip, port)

socket.create

仅 LuaSocket 3.x创建一个套接字并设置其选项

1
2
3
4
5
6
7
8
local create = require('socket').create

local sock = create("inet")
sock:setoption("reuseaddr", "on")
sock:bind("*", 12345)
local addr = sock:getpeername()
print(addr.ip, addr.port)
sock:close()

示例代码

💗💗 使用 LuaSocket 模块中的方法来进行网络通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
local socket = require('socket')

-- 创建一个 TCP 套接字
local sock = socket.tcp()

-- 连接到远程服务器
sock:connect("www.example.com", 80)

-- 发送 HTTP 请求
sock:send("GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n")

-- 接收响应
local response = sock:receive("*a")
print(response)

-- 关闭套接字
sock:close()

-- 创建一个 UDP 套接字
local udpSock = socket.udp()

-- 绑定本地端口
udpSock:bind("*", 12345)

-- 发送 UDP 数据
udpSock:sendto("Hello, UDP!", "127.0.0.1", 12345)

-- 接收 UDP 数据
local data, peer = udpSock:receivefrom(1024)
print(data, peer.ip, peer.port)

-- 关闭 UDP 套接字
udpSock:close()

-- 监听端口
local server = socket.listen(8080)

-- 接受客户端连接
local client, addr = server:accept()
print("Connected by", addr.ip, addr.port)

-- 接收客户端数据
local request = client:receive("*a")
print(request)

-- 发送响应
client:send("HTTP/1.0 200 OK\r\n\r\nHello, World!")

-- 关闭连接
client:close()
server:close()

LuaSec 库

LuaSec 是一个用于加密和安全相关的库提供了多种加密算法哈希函数随机数生成等功能它基于 OpenSSL 库因此可以在 Lua 中方便地使用高级加密功能

随机数

1
2
3
4
5
6
7
8
9
-- 生成指定长度的随机字节
local openssl = require('openssl')

local randomBytes = openssl.rand(16)
print(randomBytes) -- 输出随机生成的 16 字节

-- 生成指定长度的伪随机字节
local randomBytes = openssl.rand_pseudo_bytes(16)
print(randomBytes) -- 输出伪随机生成的 16 字节

SHA-1

1
2
local hash = openssl.sha1("Hello, World!")
print(hash) -- 输出 SHA-1 哈希值

SHA-224

1
2
local hash = openssl.sha224("Hello, World!")
print(hash) -- 输出 SHA-224 哈希值

SHA-256

1
2
local hash = openssl.sha256("Hello, World!")
print(hash) -- 输出 SHA-256 哈希值

SHA-384

1
2
local hash = openssl.sha384("Hello, World!")
print(hash) -- 输出 SHA-384 哈希值

SHA-512

1
2
local hash = openssl.sha512("Hello, World!")
print(hash) -- 输出 SHA-512 哈希值

MD5

1
2
local hash = openssl.md5("Hello, World!")
print(hash) -- 输出 MD5 哈希值

HMAC

1
2
local hmac = openssl.hmac("sha256", "secret-key", "Hello, World!")
print(hmac) -- 输出 HMAC 值

Diffie-Hellman

1
2
3
4
5
6
7
8
9
10
11
12
-- 生成 Diffie-Hellman 参数
local dh = openssl.dh_generate_parameters(1024, function(err, dh)
if not err then
print(dh:export())
else
print("Error generating DH parameters:", err)
end
end)

-- 生成 Diffie-Hellman 密钥
local key = dh:generate_key()
print(key:export())

AES

1
2
3
4
5
6
7
8
9
-- 使用 AES 加密数据
local key = openssl.rand(16)
local iv = openssl.rand(16)
local encrypted = openssl.aes_encrypt("Hello, World!", key, iv)
print(encrypted) -- 输出加密后的数据

-- 使用 AES 解密数据
local decrypted = openssl.aes_decrypt(encrypted, key, iv)
print(decrypted) -- 输出解密后的数据

PBKDF2

1
2
local key = openssl.pkcs5.pbkdf2("password", "salt", 1000, 32, "sha256")
print(key) -- 输出派生的密钥

X.509 证书

1
2
3
4
5
6
7
8
9
10
11
12
-- 创建一个 X.509 证书对象
local cert = openssl.x509.X509_new()

-- 设置 X.509 证书的主题名称
local subject = openssl.x509.X509_Name_new()
subject:add_entry_by_text("CN", "example.com", 0)
openssl.x509.X509_set_subject_name(cert, subject)

-- 设置 X.509 证书的公钥
local key = openssl.crypto.PKey_new()
key:gen_rsa(2048)
openssl.x509.X509_set_pubkey(cert, key)

crypto

1
2
3
4
5
6
7
8
9
10
11
12
-- 创建一个新的公钥/私钥对
local key = openssl.crypto.PKey_new()
key:gen_rsa(2048)
print(key:export()) -- 输出公钥/私钥对

-- 导出公钥/私钥对
local exported = key:export(nil, "password")
print(exported) -- 输出加密后的公钥/私钥对

-- 导入公钥/私钥对
local imported = openssl.crypto.PKey_import(exported, "password")
print(imported) -- 输出导入的公钥/私钥对

LuaFileSystem 库

LuaFileSystem简称 LFS是一个用于处理文件系统的库提供了比 Lua 标准 io 模块更强大的文件和目录操作功能

获取文件

1
2
3
4
5
6
7
8
9
local lfs = require('lfs')

local attr = lfs.attributes("example.txt")
print(attr.size) -- 输出文件大小
print(attr.mode) -- 输出文件模式
print(attr.ctime) -- 输出文件最后修改时间

-- 设置文件的最后访问时间为当前时间
lfs.attributes("example.txt", {atime = os.time()})

更改权限

1
lfs.chmod("example.txt", "rwxr-x---")

更改所有者和组

1
lfs.chown("example.txt", "user", "group")

复制文件

1
lfs.copy("source.txt", "destination.txt", true)

获取文件夹列表

1
2
3
4
5
local dir = lfs.dir(".")
for entry in dir do
print(entry)
end
dir.close()

查找文件在路径列表中的位置

1
2
local path = lfs.find_path("example.txt", {"./", "./data/"})
print(path or "File not found")

查找目录在路径列表中的位置

1
2
local dir = lfs.find_dir("data", {"./", "./examples/"})
print(dir or "Directory not found")

创建目录

1
lfs.mkdir("new_directory")

删除文件或目录

1
lfs.remove("example.txt")

重命名文件或目录

1
lfs.rename("oldname.txt", "newname.txt")

同步文件描述符

1
2
3
4
local file = io.open("example.txt", "w")
file:write("Some content")
lfs.sync(file)
file:close()

创建符号链接

1
lfs.symlink("example.txt", "example_link.txt")

读取符号链接的目标

1
2
local target = lfs.readlink("example_link.txt")
print(target)

创建一个临时文件名

1
2
local tmpfile = lfs.tmpname()
print(tmpfile)

创建一个带有前缀和后缀的临时文件名

1
2
local tmpfile = lfs.tempname("tmp", ".txt")
print(tmpfile)

设置文件的时间戳

1
lfs.settime("example.txt", {mtime = os.time()})

Luasql 库

luasql 是一个用于数据库访问的 Lua 库支持多种数据库驱动包括 MySQLPostgreSQLSQLiteOracle 等luasql 提供了一个统一的 API 来处理不同的数据库使得在 Lua 中进行数据库操作变得简单且一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
-- 初始化环境
local luasql = require('luasql.mysql')
local env = luasql.mysql()

-- 连接数据库
local db = env:connect('database_name', 'username', 'password')

-- 创建表
local stmt = db:execute[[CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, name TEXT)]]
stmt:close()

-- 插入数据
local insertStmt = db:execute[[INSERT INTO users (id, name) VALUES (?, ?)]]
insertStmt:bind(1, "John Doe")
insertStmt:execute()
insertStmt:close()

-- 查询数据
local queryStmt = db:execute[[SELECT * FROM users]]
local row = queryStmt:fetch({dir = luasql.FETCH_ALL})
while row do
print("ID:", row.id, "Name:", row.name)
row = queryStmt:fetch({dir = luasql.FETCH_ALL})
end
queryStmt:close()

-- 更新数据
local updateStmt = db:execute[[UPDATE users SET name = ? WHERE id = ?]]
updateStmt:bind("Jane Doe")
updateStmt:bind(1)
updateStmt:execute()
updateStmt:close()

-- 删除数据
local deleteStmt = db:execute[[DELETE FROM users WHERE id = ?]]
deleteStmt:bind(1)
deleteStmt:execute()
deleteStmt:close()

-- 关闭数据库连接
db:close()

-- 关闭环境
env:close()

Luaposix 库

luaposix 是一个用于 Lua 的库它提供了 POSIX 系统调用的封装使得开发者可以直接在 Lua 中使用 POSIX API 进行系统级编程这包括文件操作进程管理信号处理等

检查文件是否可访问

1
2
3
4
local posix = require('posix')

local isReadable = posix.access("/etc/passwd", "R_OK")
print(isReadable and "Readable" or "Not readable")

改变当前工作目录

1
posix.chdir("/tmp")

更改文件或目录的权限

1
posix.chmod("/tmp/test.txt", "rw-r--r--")

更改文件或目录的所有者和组

1
posix.chown("/tmp/test.txt", "user", "group")

关闭文件描述符

1
2
local fd = posix.open("/tmp/test.txt", "O_RDWR")
posix.close(fd)

复制文件描述符

1
local new_fd = posix.dup(fd)

获取环境变量数组

1
2
3
4
local env = posix.environ()
for k, v in pairs(env) do
print(k .. "=" .. v)
end

替换当前进程映像

1
posix.execl("/bin/ls", "ls", "-l")

更改文件描述符指向的文件的权限

1
posix.fchmod(fd, "rw-r--r--")

更改文件描述符指向的文件的所有者和组

1
posix.fchown(fd, "user", "group")

执行文件控制操作

1
2
3
local flags = posix.fcntl(fd, "F_GETFL")
local newFlags = flags | "O_APPEND"
posix.fcntl(fd, "F_SETFL", newFlags)

创建子进程

1
2
3
4
5
6
7
8
9
local pid = posix.fork()
if pid == 0 then
-- 子进程
posix.execl("/bin/echo", "echo", "Hello from child")
else
-- 父进程
local status = posix.waitpid(pid, 0)
print("Child exited with status:", status)
end

获取文件描述符指向的文件的路径配置信息

1
2
local maxLinks = posix.fpathconf(fd, "PC_MAX_CANON")
print("Maximum links:", maxLinks)

截断文件描述符指向的文件至指定长度

1
posix.ftruncate(fd, 1024)

获取当前工作目录

1
2
local cwd = posix.getcwd()
print("Current directory:", cwd)

获取环境变量的值

1
2
local path = posix.getenv("PATH")
print("PATH:", path)

获取用户的辅助组 ID 列表

1
2
3
4
local groups = posix.getgroups()
for _, gid in ipairs(groups) do
print("Group ID:", gid)
end

获取当前登录用户名

1
2
local login = posix.getlogin()
print("Login:", login)

获取当前进程的进程组 ID

1
2
local pgrp = posix.getpgrp()
print("Process Group ID:", pgrp)

获取当前进程的进程 ID

1
2
local pid = posix.getpid()
print("Process ID:", pid)

获取当前进程的父进程 ID

1
2
local ppid = posix.getppid()
print("Parent Process ID:", ppid)

获取当前进程的有效用户 ID

1
2
local uid = posix.getuid()
print("User ID:", uid)

检查文件描述符是否指向一个终端设备

1
2
local isTerm = posix.isatty(fd)
print(isTerm and "Is terminal" or "Not terminal")

发送信号给进程

1
posix.kill(pid, "SIGTERM")

创建硬链接

1
posix.link("/tmp/test.txt", "/tmp/test_link.txt")

定位文件描述符的位置

1
posix.lseek(fd, 0, "SEEK_SET")

创建目录

1
posix.mkdir("/tmp/newdir", "0755")

创建 FIFO命名管道

1
posix.mkfifo("/tmp/myfifo", "0666")

打开文件

1
local fd = posix.open("/tmp/test.txt", "O_RDWR|O_CREAT", "0644")

创建管道

1
2
local fds = {}
posix.pipe(fds)

打开一个进程供读写

1
2
3
4
local pipe = posix.popen("/bin/ls", "r")
local output = pipe:read("*a")
pipe:close()
print(output)

从文件描述符读取数据

1
2
local data = posix.pread(fd, 1024, 0)
print(data)

向文件描述符写入数据

1
posix.pwrite(fd, "Hello, world!", 0)

从文件描述符读取数据

1
2
local data = posix.read(fd, 1024)
print(data)

读取符号链接的目标

1
2
local target = posix.readlink("/tmp/symblink")
print(target)

删除空目录

1
posix.rmdir("/tmp/newdir")

设置环境变量

1
posix.setenv("TEST_VAR", "value")

注册信号处理程序

1
2
3
posix.signal("SIGINT", function(sig)
print("Caught signal:", sig)
end)

创建套接字

1
local sock = posix.socket("AF_INET", "SOCK_STREAM", "0")

获取文件的状态信息

1
2
local st = posix.stat("/tmp/test.txt")
print("Size:", st.st_size)

创建符号链接

1
posix.symlink("/tmp/test.txt", "/tmp/symblink")

同步文件系统缓冲区

1
posix.sync()

向文件描述符写入数据

1
posix.write(fd, "Hello, world!")

等待子进程结束

1
2
local status = posix.waitpid(pid, 0)
print("Child exited with status:", status)

示例代码

💗💗 使用 luaposix 模块中的方法来进行系统级编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
local posix = require('posix')

-- 创建目录
posix.mkdir("/tmp/newdir", "0755")

-- 创建文件
local fd = posix.open("/tmp/newdir/test.txt", "O_RDWR|O_CREAT", "0644")
posix.write(fd, "Hello, world!")
posix.close(fd)

-- 创建符号链接
posix.symlink("/tmp/newdir/test.txt", "/tmp/newdir/symblink")

-- 读取符号链接目标
local target = posix.readlink("/tmp/newdir/symblink")
print("Symbolic link target:", target)

-- 获取文件状态
local st = posix.stat("/tmp/newdir/test.txt")
print("File size:", st.st_size)

-- 创建子进程
local pid = posix.fork()
if pid == 0 then
-- 子进程
posix.execl("/bin/echo", "echo", "Hello from child")
else
-- 父进程
local status = posix.waitpid(pid, 0)
print("Child exited with status:", status)
end

-- 设置环境变量
posix.setenv("TEST_VAR", "value")

-- 获取环境变量
local testVar = posix.getenv("TEST_VAR")
print("Environment variable TEST_VAR:", testVar)

-- 注册信号处理程序
posix.signal("SIGINT", function(sig)
print("Caught signal:", sig)
end)

-- 同步文件系统缓冲区
posix.sync()

-- 删除目录
posix.rmdir("/tmp/newdir")

Lualanes 库

lualanes 是一个用于 Lua 的并发编程库它提供了一种轻量级的线程模型称为协程车道coroutine laneslualanes 旨在解决 Lua 中的并发问题特别是在游戏开发中它可以帮助实现更复杂的并行任务管理

创建车道

1
2
local lanes = require('lanes')
local lane = lanes.new("MainLane")

运行协程

1
2
3
lane:run(function(args)
print("Running task with args:", args)
end, "some data")

挂起协程

1
2
3
4
5
lane:run(function()
print("Yielding...")
lane:yield("yield value")
print("Resumed after yield.")
end)

挂起当前协程

1
2
3
4
5
lane:run(function()
print("Suspending...")
lane:suspend()
print("Resumed after suspend.")
end)

唤醒协程

1
2
3
4
5
6
7
lane:run(function()
print("Suspending...")
lane:suspend()
print("This line won't be printed until wakeup.")
end)

lane:wakeup("wakeup value")

恢复协程

1
2
3
4
5
6
7
8
lane:run(function()
print("Yielding...")
local res = lane:yield("yield value")
print("Resumed after yield. Got:", res)
end)

local result = lane:resume("resume value")
print("Resume result:", result)

获取协程数量

1
print(lane:coroutines())  -- 输出车道中活动协程的数量

垃圾回收

1
2
-- 清理车道中的死协程
lane:garbage_collect()

杀死所有协程

1
lane:kill()

示例代码

💗💗 使用 lualanes 模块中的方法来进行并发任务管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
local lanes = require('lanes')
local lane = lanes.new("ExampleLane")

-- 在车道中启动一个协程
lane:run(function()
print("Task started.")
print("Yielding...")
local res = lane:yield("yield value")
print("Resumed after yield. Got:", res)
print("Task finished.")
end)

-- 恢复协程并传递值
local resumeResult = lane:resume("resume value")
print("Resume result:", resumeResult)

-- 启动另一个协程
lane:run(function()
print("Another task started.")
print("Suspending...")
lane:suspend()
print("This line won't be printed until wakeup.")
end)

-- 唤醒挂起的协程
lane:wakeup("wakeup value")

-- 获取车道中当前活动的协程数量
print("Active coroutines:", lane:coroutines())

-- 清理车道中的死协程
lane:garbage_collect()

-- 杀死车道中的所有协程
lane:kill()

love2d 库

Love2D 是一个轻量级的游戏开发框架用于编写2D游戏它支持多种平台包括WindowsmacOSLinux等并且使用Lua作为脚本语言Love2D 提供了一系列模块来处理图形音频输入等让开发者能够专注于游戏逻辑而不用担心底层细节

主模块

主模块提供了游戏循环的主要入口点

💗💗 当游戏启动时调用一次用于初始化游戏资源

1
2
3
function love.load()
print("Game loaded.")
end

💗💗 每一帧调用一次用于更新游戏逻辑

1
2
3
function love.update(dt)
print("Updating game logic.")
end

💗💗 每一帧调用一次用于绘制游戏画面

1
2
3
function love.draw()
love.graphics.print("Hello, Love2D!", 100, 100)
end

💗💗 当游戏退出时调用用于清理资源

1
2
3
function love.quit()
print("Cleaning up resources.")
end

文件模块

💗💗 返回保存目录的路径

1
print(love.filesystem.getSaveDirectory())

💗💗 检查文件是否存在

1
2
3
if love.filesystem.exists("example.txt") then
print("File exists.")
end

💗💗 向文件追加一行文本

1
love.filesystem.writeline("example.txt", "Hello, Love2D!")

图形模块

💗💗 清除屏幕颜色

1
love.graphics.clear(0.1, 0.1, 0.1, 1)

💗💗 设置当前颜色

1
love.graphics.setColor(1, 0, 0, 1)

💗💗 在屏幕上打印文本

1
love.graphics.print("Hello, Love2D!", 100, 100)

💗💗 绘制矩形

1
love.graphics.rectangle("fill", 100, 100, 100, 50)

图像模块

💗💗 创建新的图像数据

1
local imgData = love.image.newImageData(100, 100)

💗💗 从数据创建图像数据

1
local imgData = love.image.newImageData(data, 100, 100, "rgba8", false)

音频模块

💗💗 创建一个新的音源

1
local sound = love.sound.newSource("sound.wav", "static")

💗💗 播放音源

1
sound:play()

键盘模块

💗💗 检查按键是否按下

1
2
3
if love.keyboard.isDown("escape") then
love.event.quit()
end

鼠标模块

💗💗 检查鼠标按钮是否按下

1
2
3
if love.mouse.isDown("left") then
print("Left mouse button is down.")
end

💗💗 获取鼠标当前位置

1
2
local mouseX, mouseY = love.mouse.getPosition()
print(mouseX, mouseY)

窗口模块

💗💗 设置窗口标题

1
love.window.setTitle("My Game")

💗💗 设置窗口模式

1
love.window.setMode(800, 600, {fullscreen=false})

物理模块

💗💗 创建新的刚体

1
local body = love.physics.newBody(world, 100, 100, "dynamic")

💗💗 创建新的形状

1
local shape = love.physics.newShape(body, "rectangle", 0, 0, 10, 10)

定时器模块

💗💗 获取当前时间

1
print(love.timer.getTime())

💗💗 获取上一帧的时间差

1
print(love.timer.getDelta())

示例代码

💗💗 使用 Love2D 模块中的方法来进行基本的游戏开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function love.load()
-- 初始化游戏
love.window.setTitle("Hello, Love2D!")
love.window.setMode(800, 600, {fullscreen=false})
love.graphics.setBackgroundColor(0.1, 0.1, 0.1, 1)
end

function love.update(dt)
-- 更新游戏逻辑
if love.keyboard.isDown("escape") then
love.event.quit()
end
end

function love.draw()
-- 渲染游戏画面
love.graphics.setColor(1, 0, 0, 1)
love.graphics.print("Hello, Love2D!", 100, 100)
love.graphics.rectangle("fill", 100, 100, 100, 50)
end

function love.quit()
-- 清理资源
print("Game has quit.")
end

设计模式

单例模式

单例模式确保一个类只有一个实例并提供一个全局访问点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local Singleton = {}
Singleton.__instance = nil

function Singleton.getInstance()
if Singleton.__instance == nil then
Singleton.__instance = {}
setmetatable(Singleton.__instance, Singleton)
end
return Singleton.__instance
end

function Singleton:doSomething()
print("Doing something...")
end

local singletonInstance = Singleton.getInstance()
singletonInstance:doSomething()

工厂模式

工厂模式提供了一个创建对象的接口但允许子类决定实例化哪一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local Factory = {}

function Factory:createObject(objectType)
if objectType == "ConcreteObjectA" then
return setmetatable({name = "ConcreteObjectA"}, ConcreteObjectA)
elseif objectType == "ConcreteObjectB" then
return setmetatable({name = "ConcreteObjectB"}, ConcreteObjectB)
else
error("Invalid object type")
end
end

local ConcreteObjectA = {}
function ConcreteObjectA:getName()
return self.name
end

local ConcreteObjectB = {}
function ConcreteObjectB:getName()
return self.name
end

local objectA = Factory.createObject("ConcreteObjectA")
print(objectA:getName())

观察者模式

观察者模式定义了对象间的一种一对多依赖关系当一个对象的状态发生改变时所有依赖它的对象都会得到通知并自动更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
local Subject = {}
Subject.observers = {}

function Subject:addObserver(observer)
table.insert(self.observers, observer)
end

function Subject:removeObserver(observer)
for i, ob in ipairs(self.observers) do
if ob == observer then
table.remove(self.observers, i)
end
end
end

function Subject:notifyObservers()
for _, observer in ipairs(self.observers) do
observer:update(self)
end
end

local Observer = {}
function Observer:update(subject)
print("Observer notified by subject: " .. tostring(subject))
end

local subject = {}
setmetatable(subject, Subject)

local observer = {}
setmetatable(observer, Observer)

subject:addObserver(observer)
subject:notifyObservers()

学习资源