概述
关于闭包,模块和包的概念。
Lua中的闭包、模块和包都是用来组织和封装代码的机制。
闭包
闭包是指一个函数和其相关的环境变量的组合。当一个函数在其外部定义的环境中使用某些变量时,这些变量将会保存在闭包中,使得函数可以在其定义之外的地方继续使用这些变量。使用闭包可以实现很多高级的编程技巧,例如函数工厂、延迟计算等。
--闭包案例:url解码
str = "word=%22%E4%BD%A0%E5%A5%BD%22;name=%22%E5%BC%A0%E4%BC%9F%22"
function urldecode(s)
s = string.gsub(s,"+"," ")
s = string.gsub(s,"%%(%x%x)",function (h)
return string.char(tonumber(h,16))
end)
return s
end
print(urldecode(str))
--输出 word="你好";name="张伟"
模块和包
Lua5.1以后,引入了模块和包
可以将其他lua文件作为模块,让当前lua文件调用
模块和包相关函数
dofile、loadfile和require 的区别
在Lua语言中,dofile、loadfile和require都是用于加载和执行Lua代码的函数,但它们之间有一些区别。
dofile函数是最简单的加载和执行Lua代码的方式。它接受一个文件路径作为参数,并返回执行结果。dofile函数会直接加载并执行指定路径的Lua脚本文件,返回脚本的执行结果。每次调用dofile函数都会重新加载和执行脚本文件。
loadfile函数用于加载Lua代码块并返回一个函数。它接受一个文件路径作为参数,并返回一个函数对象,该函数可以用于多次执行该代码块。loadfile函数只加载并编译脚本文件,不会立即执行代码。调用返回的函数对象可以多次执行代码块,每次执行返回执行结果。
require函数是用于加载和执行Lua模块的函数。它接受一个模块名作为参数,并返回模块的返回值。require函数先检查指定的模块是否已经加载,如果已经加载则直接返回模块的返回值。如果模块尚未加载,则会搜索Lua模块路径来查找并加载对应的Lua文件。require函数只会加载和执行模块一次,后续调用require函数会直接返回缓存的模块返回值。
需要注意的是,dofile和loadfile可以加载任意的Lua代码文件,而require函数则只能加载具有特定规范的Lua模块文件。
案例:
如果模块名和文件名不一致,那么需要将模块所在的文件放到Lua根目录下的lua文件夹里面
如果模块名和文件名一致,则可以将模块所在的文件和当前文件放在同一个文件夹下,所以,建议让模块名和文件名保持一致
例如模块名 叫 model ,那么文件名叫 model.lua为一致
否则模块会require调用失败
require
--model.lua
local model = {
}
model.wwww = function ()
print("test")
end
return model --这里不要忘记return
--test.lua
local mod =require("model") --可以自定义一个本地的名字
mod.wwww()
--输出 test
dofile
读取文件并直接运行
--block.lua
print("here is block")
local function block()
print("bolck fun is doing")
end
return {
fun=block}
--test.lua
local f = dofile("block.lua")
f.fun()
--输出
here is block
bolck fun is doing
loadfile
需要手动执行函数
--block.lua
print("here is block")
local function block()
print("bolck fun is doing")
end
return {
fun=block}
--test.lua
local f =loadfile("block.lua","bt")
if f then
f()
print("----------")
f().fun()
end
--输出
here is block
----------
here is block
bolck fun is doing
模式匹配
模式串含义
--1.字符模式类型
. 任意字符
%c 控制字符
%s 空白字符
%g 除空格外的可打印字符
%d 十进制数字
%x 十六进制数字
%a 字母
%l 小写字母
%u 大写字母
%w 字母和数字
%p 标点符号
这些模式串的大写表示他们的补集,例如
%a代表字母,%A则代表非字母的字符
--2.魔法字符
主要有一下这些属于魔法字符,用于拓展上面的含义
() . % + - * ? [ ] ^ $
其中:
%用来转义,%% 表示匹配一个百分号,%?匹配一个问号
[] 表示字符需要符合中括号里面的匹配要求,例如[a-z]表示匹配字符需要属于a-z里面的一个;[01]表示匹配0或者1
[]和()大多用来限制模式串的匹配范围
? 字符出现零次或一次
+ 字符重复一次或多次
* 字符重复零次或多次
- 字符重复零次或多次
详细可参考通用的模式匹配规则
string.find
查找某个字符串在第几个位置,从1开始
string.find("hello world","wor") --输出 7 9 表示第7到第9个字符串
string.find("hello world","war") --输出 nil
string.gsub
找到某个字符并替换为指定字符
或者找到某个匹配的模式,替换为指定字符
-- 查找 l 字符, 用 . 替代,输出替换后的字符串,并且输出替换的个数
string.gsub("hello world","l",".") --输出 he..o wor.d 3
-- 查找 ll 字符,用 . 替代
string.gsub("hello world","ll","..") --输出 he..o world 1
string.gsub("hello world","ll",".") --输出 he.o world 1
str= "this 8is 5a line"
su = string.gsub(str,"%d","")
print(su)
--输出 this is a line
--url解码
str = "word=%22%E4%BD%A0%E5%A5%BD%22;name=%22%E5%BC%A0%E4%BC%9F%22"
function urldecode(s)
s = string.gsub(s,"+"," ")
s = string.gsub(s,"%%(%x%x)",function (h)
return string.char(tonumber(h,16))
end)
return s
end
print(urldecode(str))
--输出 word="你好";name="张伟"
string.match
在目标字符串中进行模式串的搜索
str = "hello 09/09/1999"
m = string.match(str,"%d+/%d+/%d+")
print(m)
--输出 09/09/1999
string.gmatch
在目标字符串中进行模式串的搜索,找出所有的生成一个表,用来遍历
str= "this is a line"
word={
}
for w in string.gmatch(str,"%a+") do
word[#word+1] = w
end
for k,value in pairs(word) do
print(value)
end
--输出
this
is
a
line
日期和时间
os.time
表示1970年1月1日0点0分0秒到当前的秒值,其他时区见下方代码
print(os.time())
--给它一个参数,表示计算当前年月日到1970.1.1 0点0分0秒的秒数
print(os.time({
year=1970,month=1,day=1,hour=0,min=0,sec=0}))
--这一步有人发现os.time给参数报错了,原因是可能你不在国际时间的0时区
--如果你在东8区,那么起始的0值是
--1970.1.1 8点0分0秒
--也就是说你要修改参数为一下时间及之后才可以正确输出
print(os.time({
year=1970,month=1,day=1,hour=8,min=0,sec=0}))
--输出 0
os.date
输出当前的日期,可以用指示符参数进行控制日期的输出格式
--1.常规用法
print(os.date())
--输出当前日期
--2.指示符简介
--2.1常用指示符
%Y 年份 例如1909
%y 年份简写 例如09
%m 月份
%d 日
%H 24小时制的小时
%I 12小时制的小时
%M 分钟
%S 秒
--2.2不常用指示符
%c 月日年和时间
%d 一个月中的第几天
%j 一年中的第几天
%w 星期几 数字表示
%W 一年中的第几周
%p am或者pm
%a 星期几英文简写
%A 星期几英文全称
%b 月份英文简写
%B 月份英文全称
--3.基本使用方式
print(os.date("%Y-%m-%d"))
--输出当前时间
--4.指定秒值输出指定格式
t=os.time()
print(os.date("%Y-%m-%d",t))
print(t)
--上面形式的简写
print(os.date("%Y-%m-%d",os.time()))
os.difftime
用来计算两个秒值相差多少秒,如果前面的比后面的小,那么输出的是负数
t1 = os.time({
year=1997,month=1,day=1,hour=8,min=0,sec=1})
t2 = os.time({
year=1997,month=1,day=1,hour=8,min=0,sec=0})
print(os.difftime(t1,t2))
--输出 1.0
位运算
位运算符有
&(按位与) |(按位或) ~(按位异或) >>(逻辑右移) <<(逻辑左移)
print(string.format("%x",0xff<<8))
--等价于
print(string.format("%x",0xff>>-8))
编译、执行和错误处理
1.预编译
--block.lua
print("here is block")
打开文件位置,使用下面命令进行预编译
luac -o block.lc block.lua
--此时产生中间文件block.lc
--再执行
lua block.lc
--输出 here is block
2.错误处理
lua 中的 pcall 和 xpcall 是用来捕获和处理错误的函数。
pcall 函数的基本用法为:
function pcall(f, ...)
-- 尝试执行函数 f,并将其参数传递给它
-- 如果执行过程中没有发生错误,则返回 true 和函数的返回值
-- 如果发生错误,则返回 false 和错误信息
end
xpcall 函数的基本用法为:
function xpcall(f, err_func, ...)
-- 尝试执行函数 f,并将其参数传递给它
-- 如果执行过程中没有发生错误,则返回 true 和函数的返回值
-- 如果发生错误,则调用 err_func 函数处理错误,并返回 false 和处理后的结果
end
pcall 和 xpcall 函数的区别在于,xpcall 函数可以指定一个错误处理函数 err_func,在发生错误时调用该函数来处理错误。err_func 函数接受一个参数,即错误信息,并返回处理后的结果。
以下是 pcall 和 xpcall 的用法示例:
-- pcall 示例
local function getstr(str)
if type(str)~="string" then
error("it is not a string")
end
print(str)
end
local success, result = pcall(getstr, 10)
if success then
print("Result:", result)
else
print("Error:", result)
end
--输出
Error: \test.lua:3: it is not a string
-- xpcall 示例
local function getstr(str)
if type(str)~="string" then
error("it is not a string")
end
print(str)
end
local function handle_error(err)
print("Error:".."print in handle", err)
return 0
end
local success, result = xpcall(getstr, handle_error, 10)
print("Result:", result)
--输出
Error:print in handle \test.lua:3: it is not a string
Result: 0
在以上示例中,当调用 divide 函数时,如果除数为0,则会抛出一个错误。通过使用 pcall 函数,在调用 divide 函数时捕获错误,并根据返回值来判断是否发生了错误。
而在使用 xpcall 函数时,除了能够捕获错误外,还可以指定一个错误处理函数 handle_error 来处理错误。在错误发生时,会调用 handle_error 函数,并将错误信息作为参数传递给它。在 handle_error 函数中,可以根据需要进行错误处理,并返回一个处理后的结果。