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 SDK、LÖ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 为 repl。n 指定替换的最大次数。
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

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

LuaJIT的特点

  1. 即时编译(JIT):LuaJIT能够在运行时将热点代码(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 是 LuaJIT(Just-In-Time Compiler for Lua)的一个重要特性,允许 Lua 脚本直接调用 C 语言编写的函数,并支持在 Lua 中使用 C 数据类型。ffi(Foreign 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 库,支持多种数据库驱动,包括 MySQL、PostgreSQL、SQLite、Oracle 等。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 lanes)。lualanes 旨在解决 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游戏。它支持多种平台,包括Windows、macOS、Linux等,并且使用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()

学习资源

  • 文档
    • 【官方文档】https://lua.org/manual/5.4/
    • 【菜鸟教程】https://www.runoob.com/lua/lua-basic-syntax.html
  • 视频
    • 【C#与Lua语言基础—零基础入门】https://www.bilibili.com/video/BV1FF411K7qj