笔记 | Programming in Lua

Posted 5ithink

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了笔记 | Programming in Lua相关的知识,希望对你有一定的参考价值。

词法约定

1.标识符:以字母或者下划线开头+字母/下划线/数字

2.保留字:

[and break do else elseif end false for function if in local or not nil repeat return then true until while]

3.规范约束

1.语句结尾用";"隔开非必须

2.大小写敏感

3.单行注释:--

4.多行注释:--[[XXXXX--]]

5.\ 转义:\[option] 例如:\[a|b|f|n|r|t|v|\|"|'|[|]]

6.[[......]]可以保证字符串原样输出

类型和值

  • nil:a.访问未声明的全局变量返回:nil b.删除全局变量可以赋值:nil

  • boolea:a.值域[true|false] b.lua 所有值均可作为条件,(nil 和false)为假 ,其他为真

  • number

  • string:a.8位字节,自动分配内存,高效处理长字符串 b.单引号或者双引号表示

  • function

  • table

  • thread

  • userdata

注:

1.lua中数字可以在number和string之间自动进行类型转换

2.lua是动态语言,变量不需要定义类型

3.'..' 字符串连接符

4.tonumber函数:字符串转数字 ,非数字字符串返回nil

5.tostring函数:数字转字符串

运算符和表达式

1.算术运算符

  • 二元运算符:+ - * / ^ (加减乘除幂)

  • 一元运算符:-(负值符号)

2.关系运算符:[<|<=|>|>=|==|~=]

  • 比较结果:true/false nil==nil

3.逻辑运算符:[and | or | not]

  • [false 和 nil 为假,其他为真]

  • a and b 如果a为false 则返回a ,否则返回b

  • a or b   如果 a为true 则返回a,否则返回b

  • and 优先级高于or

  • not的结果返回:true | false

4.连接运算符:.. (如果是数字会自动转为字符串再进行连接)

5.优先级:从左到右优先级递减

    ^ > [not  -] > [*  /] > [+  -] > .. > [<  >  <=  >=  ~=  ==] > and > or

--算术运算符
a1 = 1+2+3+4+5+6+7+8+9+10 print(a1) -- 55
a2 = (1+10)*10/2 print(a2) -- 55
--关系运算符
a = {}
a.x = 1
a.y = 2

b = {}
b.x = 1
b.y = 2

c = a  
print
(a == b ) -- false
print(a == c ) -- true
print(0 < 1) -- true     数字比较大小
print("2" < "15") -- false    按字母顺序进行比较,依赖本地环境
print(1 < "1") -- error attempt to compare number with string

--逻辑运算符 and or not
print(4 and 5) -- 5
print(nil and 3) -- nil
print(false and 3) -- false
print(4 or 5) -- 4
print(false or 5) -- 5

--eg1:如果x为:false 或者 nil 则把y值赋值给x
res = x or y  print(res)
--eg2:lua三目运输符实现 a?b:c <=>(a and b) or c
res = (a and b) or c print(res)

print(not nil) -- true
print(not false) -- true
print(not 0) -- false
print(not not nil) -- false

基本语法

1.局部变量与全局变量:局部变量申明local关键字,全局变量申明可省略

2.赋值语句

  • Lua可以同时对多个变量赋值,分隔符为逗号:,

  • 变量个数>值个数[按照变量个数补足nil]

  • 变量个数<值个数[多余值被忽略]

3.控制语句

  • if语句

  • if-else语句

  • if-elseif-else语句

  • while语句

  • repeat-until语句

  • for 语句

  • break 语句

  • return 语句

注:

1.for var = exp1,exp2,exp3 do end

exp1 初始值,局部变量

exp2 终止值,仅仅执行一次

exp3 step 默认1

--变量赋值
t = {}
a, b = 10, 20
print(a,b) -- 10 20 变量个数与值个数相同

a, b, c = 0, 1
print(a,b,c) -- 0 1 nil 变量个数大于值个数

a, b = 5
print(a,b,c) -- 5 nil nil 变量个数小于值个数
--值交换
x = 1 y = 2
x, y = y, x
print
(x,y) -- 2 1
--while语句
x = 10
local i = 1
while i<=x do local x = i*2 i = i + 1 end
--if-esle语句
if i > 20 then
   local
x= 20
   
print(x + 2)
else
   
print(x)
end
--for语句
for i = 1, 10, 2 do print(i) end
--遍历数组
a = {"java","c++","c","lua"}
for i,v in ipairs(a) do print(i .."="..v) end

函数

1.普通函数

a.函数定义:function

b.函数调用:f()

c.函数参数: ... 表示可变参数存放在arg表中 ,arg.n表示参数个数

d.多值返回:没有返回值默认为nil,可以将unpack函数返回结果作为参数

e. 哑元:_

f.词法界定:嵌套函数可以访问外部函数变量

g.非全局函数定义&声明

    local f =function(...) ... end

    local function f(...) ... end

    local  f

    function f(...) ... end

h.尾调用:函数最后调用其他函数(尾调用程序栈中不需要保存调用者信息,因此不需要占用栈空间)

比如: return f(...) 类似 goto函数避免栈溢出

a = { "hello", "lua!" } print(unpack(a))    --hello lua!

f = string.find print(f(unpack(a)))

str = ""
function f(a,b,...)
print(a,b,arg.n) --1    2  5
   
for i,v in ipairs(arg) do
       
str = str .. tostring(v) .. "\t"
   
end
   
str = str .. "\n"
end
f(1,2,3,4,'a','b','c') print(str) --3    4  a  b  c

-- 亚元:'_'
local _, x = string.find("hello 5ithink!", "think")
print(_,x) --9    13

2.闭包

闭包:可以访问外部局部变量(external local variable)或者upvalue,回调函数中闭包功能比较有用。

闭包是一个内部函数,可以访问外部局部变量,每次闭包重新调用,外部局部变量值均会保存;

闭包结构:外部变量+闭包函数+重复调用

function count()
local i = 0
   
return function()
i = i + 1
   
return i
   
end
end
c1 = count()
print(c1()) --i 的值 1
print(c1()) --i 的值 2
print(c1()) --i 的值 3
c2 = count()
print(c2()) --i 的值 1
c3 = count()
print(c3()) --i 的值 1

3.迭代器for

无状态迭代器:不保留任何状态的迭代器,避免额外开销,只需要k/v即可取集合中元素

for k, v in pairs(t) do  print(k, v) end

--迭代器
function f(...)
   
for k, v in ipairs(arg) do
       
print(k .. v)
   
end
end
f(1,2,3,'a','b','c','d')

协同程序

1.协同程序(coroutine)与线程比较:

  • 相同之处:有自己的堆栈,全局变量,指令指针,与其他协同程序共享全局变量

  • 不同之处:在多处理器中多线程可以同时运行多个线程,协同程序在任一时刻只有一个协同程序运行

2.协同与unix中的管道比较:

  • 相同之处:协同是一种非抢占式多线程,类似unix中的管道

  • 不同之处:管道每个任务在独立进程中运行,协同每个任务运行在独立的协同代码中;管道在读(consume写(produce)中提供缓冲避免进程切换代价开销协同模式下任务切换代价小等同调用函数,性能和效率比较高

3.协同函数

a.协同函数封装在coroutine中

b.协同状态:挂起(suspended) 运行态(running) 停止态(dead)

  • creat:创建,参数通常为匿名函数,创建成功返回thread类型

  • status:状态

  • resume:运行

  • yield:挂起

--creat coroutine --
co = coroutine.create(function ()
print("hi,lua") end)

--co 类型:thread
print(type(co))
--creat状态:suspended
print(coroutine.status(co))

--resume状态:running
coroutine.resume(co)

--执行完成状态:dead
print(coroutine.status(co))

注:

1.Lua中的协同是非对称协同asymmetric coroutines

2.使一个执行的协同函数挂起不能等价于使一个被挂起的函数再次执行

3.对称协同:使用同一函数在挂起/执行切换状态

4.当激活时,程序会从挂起的地方重新开始执行

5.当程序已经执行完成时,调用resume程序会返回false和错误信息,如果执行程序出现异常,Lua并不会抛出错误而是将错误返回给resume函数

--创建coroutine程序
co = coroutine.create(function ()
for i=1,2 do
       
print("co", i)
coroutine.yield()
end
end
)
--运行协同程序 打印结果:co  1
coroutine.resume(co)
--挂起状态:suspended
print(coroutine.status(co))
--激活时从挂起地方开始执行 打印结果:co 2
coroutine.resume(co)
--挂起状态:suspended
print(coroutine.status(co))
coroutine.resume(co)
--执行完成状态:dead
print(coroutine.status(co))
--执行完成调用resume函数,抛出异常:false    cannot resume dead coroutine
print(coroutine.resume(co))

1.通过yield/resume交换数据

--resume传参给协同主程序
co = coroutine.create(function (a,b,c)
print("co", a,b,c)
end)
coroutine.resume(co, 1, 2, 3) --co   1  2  3

--数据由yield传递给resume
co = coroutine.create(function (a,b)
coroutine.yield(a + b, a - b)
end)
print(coroutine.resume(co, 20, 10)) --true 30 10
coroutine.resume(co)
print(coroutine.resume(co)) --false cannot resume dead coroutine

--数据由resume传递给yield
co = coroutine.create (function (...)
print("co参数num:".. arg.n, coroutine.yield())
end)
coroutine.resume(co, 1, 2) --co参数num:2
print(coroutine.resume(co)) --true

--协同程序返回值传递给resume
co = coroutine.create(function ()
return 6, 7 end)
print(coroutine.resume(co)) --true 6  7

2.消费者驱动设计

--消费者 receive
function receive()
local status, value = coroutine.resume(producer)
--receive: status:    true   msg:2
   
print("receive:","status:",status,"msg:" .. value) ;
   
return value
end

function
send(x)
print("send msg:" .. x) --(1)send msg:1 (2)send msg:2
   
coroutine.yield(x)
end
i = 1 ;
--生产者 producer
producer = coroutine.create(function()
while true do
       
print("count:".. i)
local x = io.read()
send(x)
i = i + 1
   
end
end
)

--消费驱动设计
while true do
   
receive()
end

coroutine.resume(producer)

3.过滤器扩展:过滤器对数据进行转换处理

function receive(producer)
local status, value = coroutine.resume(producer)
return value
end

function
send(x)
coroutine.yield(x)
end

function
producer()
return coroutine.create(function()
       
while true do
           local
x = io.read()
           send(x)
       
end
   end
)
end

function
consumer(prod)
while true do
       local
x = receive(prod)
if x == nil then break end
       
io.write(x, "\n")
end
end

function
filter(prod)
return coroutine.create(function()
       
local line = 1
       
while true do
           local
x = receive(prod)
           
if x == nil then break end
           
x = string.format("%5d %s", line, x)
           send(x)
           line = line +
1
   
end
   end
)
end
consumer(filter(producer()))

数据结构

1.数组/多维数组/矩阵:大小不固定,可以动态增长 下标建议从1开始

--数组
a = {}
for i=1, 1000 do  a[i] = 0 end
print(table.getn(a))
--创建并初始化
b = {1,2,4,5,5,6,4}
for i,v in pairs(b) do
   
print(i..v)
end
--创建多维数组
m =4
n =4
mt = {}
for i=1,m do
   
mt[i] = {}
for j=1,n do
       
mt[i][j] = i
   
end
end
--获取多维数组元素
res = {}
for i=1,n do
   for
j=1,m do
       local
tmp = mt[i][j]
res[i*m+j] = tmp
       
print(i,j,i*m+j,mt[i][j])
end
end
print(unpack(res))

2.栈

List = {}
function List.new()
return { first = 0, last = -1 }
end

function
List.pushleft(list, value)
local first = list.first - 1
   
list.first = first
   
list[first] = value
end

function
List.popleft(list)
local first = list.first
   
if first > list.last then error("list is empty") end
   local
value = list[first]
list[first] = nil

   
list.first = first + 1
   
return value
end

function
List.pushright(list, value)
local last = list.last + 1
   
list.last = last
   
list[last] = value
end

function
List.popright(list)
local last = list.last
   
if list.first > last then error("list is empty") end
   local
value = list[last]
list[last] = nil
   
list.last = last - 1
   
return value
end

--栈模拟:左入栈-先进后出:入栈:1,2,3 出栈:3,2,1
l  = List.new()
List.pushleft(l,"1")
List.pushleft(l,"2")
List.pushleft(l,"3")

print(l.first,List.popleft(l))
print(l.first,List.popleft(l))
print(l.first,List.popleft(l))

--栈模拟:右入栈-先进后出:入栈:1,2,3 出栈:3,2,1
l2  = List.new()
List.pushright(l2,"r1")
List.pushright(l2,"r2")
List.pushright(l2,"r3")

print(l2.last,List.popright(l2))
print(l2.last,List.popright(l2))
print(l2.last,List.popright(l2))

3.链表:每个节点可以是一个table:包含2个域(指针/值)

--链表:指针和值
l4 = { next = nil, value = "end" }
l4 = { next = l5, value = "v4" }
l3 = { next = l4, value = "v3" }
l2 = { next = l3, value = "v2" }
l1 = { next = l2, value = "v1" }
l = { next = l1, value = "begin" }

while l do
   
print(l.next, l.value)
l = l.next
end

4.队列/双端队列:table库中insert /remove实现队列操作

--table insert/remove实现队列
l = {}
table.insert(l, 1, "v1")
table.insert(l, 2, "v2")
table.insert(l, 3, "v3")
table.insert(l, 4, "v4")

print(unpack(l)) --v1   v2 v3 v4
print(table.maxn(l))

for i, v in ipairs(l) do
   
print(i,v)
if i==2 then
       
table.remove(l,i)
end
end

5.集合

--定义集合
function Set(list)
local set = {}
for _, l in ipairs(list) do
       
set[l] = true
       
--print(_, l)
   
end
   return
set
end

--定义表
list = { "while", "end", "function", "local", "for"}

--表转换成集合
reserved = Set(list)
for i, v in ipairs(list) do
   
print(i, reserved[v],v)
end

注:table是lua中唯一数据结构。常见数据结构:array,record,list,set,quene都可以通过table实现

Metatable+Metamethod

1.metatable

a.lua通过metatable对集合进行操作

b.lua创建的表metatable默认为nil

c.setmetatable函数可以改变一个表的metatable

d.表都可以是其他表的metatable,可以共享同一个metatable,也可以为自身metatable

e.集合比较大小

--集合操作
Set = {}
Set.mt = {}

function Set.new(t)
local set = {}
setmetatable(set, Set.mt)
for _, l in ipairs(t) do
       
set[l] = true
   
end
   return
set
end

function
Set.union(a, b)
local res = Set.new {}
for k in pairs(a) do
       
res[k] = true
   
end
   for
k in pairs(b) do
       
res[k] = true
   
end
   return
res
end

function
Set.intersection(a, b)
local res = Set.new {}
for k in pairs(a) do
       
res[k] = b[k]
end
   return
res
end

function
Set.tostring(set)
local s = "{"
   
local sep = ""
   
for e in pairs(set) do
       
s = s .. sep .. e sep = ", "
   
end
   return
s .. "}"
end

function
Set.print(s)
print(Set.tostring(s))
end
--比较metatable
s1 = Set.new({ 1, 2, 3, 4 })
s2 = Set.new({ 6, 5, 3, 4 })
print(getmetatable(s1))
print(getmetatable(s2))
--添加__add(加) __mul __sub(减)、__div(除)、__unm(负)、__pow(幂)
Set.mt.__add = Set.intersection
Set.mt.__mul = Set.union
s3 = s1 + s2
print
(Set.print(s3))

s4 = (s1 + s2) * s2
print
(Set.print(s4))
--__eq(等于),__lt(小于),和__le(小于等于 Lua将a ~= b转换为not (a == b);a > b转换为b < a;a >= b转换为 b <= a
--<= b表示集合a是集合b的子集。这种意义下,可能a <= b和b < a都是false;因此,我们需要将__le和__lt的实现分开
Set.mt.__le = function(a, b)
for k in pairs(a) do
       if not
b[k] then
           return
false
       
end
   end
   return
true
end
Set.mt.__lt = function(a, b)
return a <= b and not (b <= a)
end
Set.mt.__eq = function(a, b) return a <= b and b <= a end

s1 = Set.new { 2, 4 } s2 = Set.new { 4, 10, 2 }
print(s1 <= s2)
print(s1 < s2)
print(s1 >= s1)
print(s1 > s1)
print(s1 == s2 * s1)

--自定义tostring重写默认tostring函数
Set.mt.__tostring = Set.tostring

--可以设置__metatable的值,防止其他使用者修改
Set.mt.__metatable = "no your business"
print(getmetatable(s1))
--setmetatable(s1, {})

2.Metamethod

a.__index:可以是函数也可以是表;当访问域不存在,lua解释器会从__index查找Metamethod,有则返回,无返回nil

  • __index为函数时:lua会将table和缺省的值作为参数调用__index Metamethod;

  • __index为表时:lua会查询表中是否有缺省的域;

b.__newindex:用来对表更新

1.有默认值的表:为表中所有缺省的域设置默认值

--__index 函数
Window = {}
Window.prototype = {x=0, y=0, width=100, height=100 }
Window.mt = {}

function Window.new (o)
setmetatable(o, Window.mt)
return o
end
--函数模式
Window.mt.__index = function (table, key)
return Window.prototype[key]
end
--表模式
Window.mt.__index = Window.prototype

--当w访问的域不存在,会通过__index指定的表中寻找
w = Window.new{x=10, y=20}
print(w.width) --w 中不存在width域,会从__index中搜寻返回:100
print(w.x) --w 中存在x域,返回 10

2.监控表:监控表的访问以及修改,通过__index 和__newindex实现

--监控一个表
t = {}
local _t = t

proxy
= {}
local mt = {
__index = function(t, k)
print("*access to element " .. tostring(k)) --*access to element 2
       
return _t[k]
end,
   
__newindex = function(t, k, v) --*update of element 2 to hello lua
       
print("*update of element " .. tostring(k) .. " to " .. tostring(v))
_t[k] = v
   
end
}
setmetatable(proxy, mt)
proxy[2] = "hello lua"
print(proxy[2])

3.只读表:通过代理思想实现,当试图修改只读表时抛出异常

--只读表 error的第二个参数2,将错误信息返回给企图执行update的地方
function readOnly(t)
local proxy = {}
local mt = {
__index = t,
       
__newindex = function(t, k, v) error("attempt to update a read-only table", 2)
end
   
}
setmetatable(proxy, mt)
return proxy
end

days = readOnly({ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" })
days[2] = "Noday"       --attempt to update a read-only table
print(days[1])

Lua面向对象程序设计

1.包:避免不同库文件命名冲突问题的机制,各自命名空间内部定义名字相互之间没影响。Lua通过表实现package机制

a.明确包名和文件名的关系

b.require 是引用文件名而不是package

注:

1.如果函数经常被使用,可以定义成局部变量

2.如果包名过长需要重写,可以定义局部变量简写

3.如果担心包名有冲突,可以在使用前先检查是否已存在

--方式 1:使用表来实现包 缺点:每个函数前都需要加包名:a
a = {}
a.i = 1
function a.add() end
function
a.sub() end
return
a

--方式 2  使用固定局部变量优化前缀 可以不用依赖包名
local A = {}
a = A
A
.i = 1
function A.add() end
return
a
--方式3 私有成员:私有部分仅自身可见,消除函数调用需加包名冗余
local i = 1
local function add()
print(i) ;
end

local function
sub() end

local function
mul() end

complex = {
i = i,
   
add = add,
   
sub = sub,
   
mul = mul,
}
return complex
--定义moudle
lib2 = {}
function lib2.a(a)
print("lib2:" .. a)
end
return
lib2

--引用moudle
package
.path = package.path .. ";./?.lua"
local c = require("c")
c.a("c")
local l = require("chapter/lib2")
l.a("a");
--对象
Account = {
balance = 10,
   
withdraw = function(self, v) self.balance = self.balance - v end
}
--self指调用方法对象 相当于Java this 可以用:替换隐藏该参数(冒号的效果相当于在函数定义和函数调用的时候,增加一个额外的隐藏参数)
function Account.test(self, v)
self.balance = self.balance - v
   
print(self.balance)
end

function
Account:withdraw(v)
self.balance = self.balance - v
   
print(self.balance)
end
--调用 a1:withdraw(100) 等效于a1.withdraw(a1,100)
a1 = Account;
a1:withdraw(100)
a1.withdraw(a1, 100)
--继承
function Account:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
   
return o
end

function
Account:deposit(v)
self.balance = self.balance + v
   
print(self.balance)
end

function
Account:withdraw(v)
if v > self.balance then
       
error "insufficient funds"
   
end
   
self.balance = self.balance - v
   
print(self.balance)
end

b = Account:new({ balance = 100 })
print(b.balance)
b:withdraw(10)

--创建SpecialAccount类 SpecialAccount继承Account 对象s继承SpecialAccount
SpecialAccount = Account:new();
s = SpecialAccount:new { limit = 1000.00 }
s:deposit(100.00)

--重写父类 withdraw方法
function SpecialAccount:withdraw(v)
if v - self.balance >= self:getLimit() then
       
error "insufficient funds"
   
end
   
self.balance = self.balance - v
   
print(self.balance)
end

function
SpecialAccount:getLimit()
return self.limit or 0
end

s:withdraw(50)

标准库

1.Table库

--获取table大小
a = { 1, 2, 3, nil, 4 }
print(table.getn(a)) --5

a = { 1, 2, 3, nil } --3 尾部nil忽略
print(table.getn(a))

print(table.getn({ 10, 2, nil; n = 3 })) -- 2

--table库从list的任意位置插入和删除元素的函数,table.insert(a,1,15) 参数:数组/插入位置/值 默认:从最后插入值
--table.insert函数:在array指定位置插入一个元素,并将后面所有其他的元素后移
--table.remove函数删除数组中指定位置的元素,并返回这个元素,所有后面的元素前移,并且改变数组的大小 ;
--不带位置参数:删除最后一个元素
a = {}
for line in io.lines() do
   
table.insert(a, line)
end

print(table.getn(a))

--排序:table.sort默认从小到大排序
--表/数组中所有下标组成一个集合是无序的,无法对下标排序
user = {
id = 1,
   
name = '5ithink',
   
sex = 'man',
   
age = 20
}

a = {}
for n in pairs(user) do
   
table.insert(a, n)
end

table.sort(a)

for k, v in ipairs(a) do
   
print(k,v)
end

--table insert/concat函数
local t = {}
for i = 1, 10 do
   
table.insert(t, i)
end
s  = table.concat(t, "|")
print(s) --1|2|3|4|5|6|7|8|9|10

2.String库

--lua中字符串操作不改变原字符串到值
s = "abcdefACDFEF"
--返回字符串长度
local len = string.len(s) print(len)

--返回重复字符串s n次到字符串
s2 = string.rep(s, 2) print(s2)

--字符串大小写转换
s3 = string.upper(s) print(s3)
s3 = string.lower(s) print(s3)

--截取字符串:字符串[i,j],下标从1开始,最后一个为 -1,倒数第二个为-2以此类推...
s4 = string.sub(s, 2, -3) print(s4)

--string.char获取0个或多个整数,将每一个数字转换成字符,返回一个所有这些字符连接起来的字符串
--string.byte(s, i)将字符串s的第i个字符的转换成整数,i省略 默认为1
print(string.char(97))
i = 99; print(string.char(i, i + 1, i + 2))
print(string.byte("abc"))
print(string.byte("abc", 2))
print(string.byte("abc", -1))

--函数string.format在用来对字符串进行格式化
--格式化串,由指示符和控制格式的字符组成。
--指示符后的控制格式的字符可以为:十进制'd';十六进制'x';八进制'o';浮点数'f';字符串's'
--在指示符'%'和控制格式字符之间还可以有其他的选项

print(string.format("pi = %.4f",3.1415926))
d = 5; m = 11; y = 1990 print(string.format("%02d/%02d/%04d", d, m, y))
tag, title = "h1", "a title" print(string.format("<%s>%s</%s>", tag, title, tag))
--字符串查找 :在目标串中搜索指定模式的串 find(s,substr,index) index:标示目标串中搜索的起始位置
s = "hello world" i, j = string.find(s, "hello")
print(i, j)
print(string.sub(s, i, j))

print(string.find(s, "world"))

i, j = string.find(s, "l") print(i, j)
print(string.find(s, "lll"))

local t = {}
local i = 0 while true do i = string.find(s, "\n", i + 1)
if i == nil then break end table.insert(t, i)
end
--全局字符串替换 string.gsub函数有三个参数:目标串,模式串,替换串。他基本作用是用来查找匹配模式的串,并将使用替换串其替换掉
--第四个参数是可选的,用来限制替换的范围:
s = string.gsub("Lua is cute", "cute", "great") print(s)

s = string.gsub("all lii", "l", "x") print(s)

s = string.gsub("Lua is great", "perl", "tcl") print(s)

s = string.gsub("all lii", "l", "x", 1) print(s)

s = string.gsub("all lii", "l", "x", 2) print(s)

--string.gsub的第二个返回值表示他进行替换操作的次数
_, count = string.gsub("all lii", "l", "x")
print(_, count)
--全局字符串查找
--模式串
--. 任意字符
-- %a  字母
-- %c  控制字符
-- %d  数字
-- %l  小写字母
-- %p  标点字符
-- %s  空白符
-- %u  大写字母
-- %w  字母和数字
-- %x  十六进制数字
-- %z  代表0的字符
--上述字符大写形式表示小写所代表的集合的补集
--特殊字符: ) . % + - * ? [ ^ $

s = "Deadline is 30/05/1999, firm" date = "%d%d/%d%d/%d%d%d%d" print(string.sub(s, string.find(s, date)))

print(string.gsub("hello, up-down!", "%A", "."))
--'%' 用作特殊字符的转义字符,
--'%.' 匹配点;
--'%%' 匹配字符 '%'。转义字符 '%'不仅可以用来转义特殊字符,还可以用于表示所有的非字母的字符。
--模式串用于函数的时候,'%' 才作为转义字符 Lua语言转义符:'\'
--'%d' 表示 '[0-9]'
--'%x' 表示 '[0-9a-fA-F]'
--[0-7] 等效于[01234567]
--^ 表示 非 即 补集 '[^0-7]' '[^\n]' 可以使用大写的字符类表示其补集:'%S' 比 '[^%s]' 要简短些
--+  匹配前一字符1次或多次
--*  匹配前一字符0次或多次
-- - 匹配前一字符0次或多次(与 '*' 一样,但他进行的是最短匹配)
--?  匹配前一字符0次或1次
print(string.gsub("one, and two; and three", "%a+", "word"))
i, j = string.find("the number 1298 is even", "%d+")
print(i, j)
--匹配一对圆括号()或者括号之间的空白,可以使用 '%(%s*%)'
--'[_%a][_%w]*' :字母或者下划线开头的字母下划线数字序列
--模式最短匹配 于 最长匹配区别
test = "int x; /* x */  int y; /* y */"
print(string.gsub(test, "/%*.*%*/", "<COMMENT>"))
print(string.gsub(test, "/%*.-%*/", "<COMMENT>"))
--以 '^' 开头的模式只匹配目标串的开始部分,相似的,以 '$' 结尾的模式只匹配目标串的结尾部分
if (string.find(s, "^%d")) then
end

if
string.find(s, "^[+-]?%d+$") then
end
--'%b' 用来匹配对称的字符。常写为 '%bxy' ,x和y是任意两个不同的字符;x作为匹配的开始,y作为匹配的结束。
-- 比如,'%b()' 匹配以 '(' 开始,以 ')' 结束的字符串
-- 常用的这种模式有:'%b()' ,'%b[]','%b%{%}' 和 '%b<>'
print(string.gsub("a (enclosed (in) parentheses) line", "%b()", ""))
--机制:可以使用模式串的一部分匹配目标串的一部分 捕获的模式用圆括号括起来
--当匹配发生的时候,find函数先返回匹配串的索引下标,然后返回子模式匹配的捕获
p = "name = Anna"
_, _, key, value = string.find(p, "(%a+)%s*=%s*(%a+)") print(key, value
date = "17/7/1990" _, _, d, m, y = string.find(date, "(%d+)/(%d+)/(%d+)") print(k, d, m, y)
--在模式中使用向前引用,'%d'(d代表1-9的数字)表示第d个捕获的拷贝
s = [[then he said: "it's all right"!]]
a, b, c, quotedPart = string.find(s, "([\"'])(.-)%1")
print(c,quotedPart)
print(string.gsub("hello Lua!", "(%a)", "%1-%1"))
print(string.gsub("hello Lua", "(.)(.)", "%2%1"))
--捕获运用:字符串替换
s = "\command{some text}"
s = string.gsub(s, "\(%a+){(.-)}", "<%1>%2</%1>")
print(s)
s = 'he \quote{task} is to \em{change} that. '
s = string.gsub(s, "\(%a+){(.-)}", "<%1>%2</%1>")
print(s)
--去除首尾空字符 两个定位符('^' 和 '$')保证我们获取的是整个字符串
-- 两个 '%s*' 匹配首尾的所有空格,
-- '.-' 匹配剩余部分。
-- gsub返回两个值,使用额外的圆括号丢弃多余的结果(舍弃替换发生的次数)
function trim(s) return (string.gsub(s, "^%s*(.-)%s*$", "%1")) end
--应用 4:函数作为string.gsub的第三个参数调用gsub
-- string.gsub每次发现一个匹配的时候就会调用作为参数的函数,
-- 捕获值可以作为被调用的这个函数的参数,而这个函数的返回值作为gsub的替换串
function expand(s) return (string.gsub(s, "$(%w+)", function(n)
return tostring(_G[n])
end))
end
print(expand("print = $print; a = $a"))
--应用5:URL解码/编码
name = "al"; query = "a+b = c"; q = "yes or no"
function unescape(s) s = string.gsub(s, "+", " ")
s = string.gsub(s, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end) return s
end
print(unescape("a%2Bb+%3D+c"))
cgi = {}
function decode(s)
for name, value in string.gfind(s, "([^&=]+)=([^&=]+)") do
       
name = unescape(name)
value = unescape(value)
cgi[name] = value
       
print(name, value)
end
end
decode("name=al&query=a%2Bb+%3D+c&q=yes+or+no")
print(cgi["q"])
function escape(s)
s = string.gsub(s, "([&=+%c])", function(c)
return string.format("%%%02X", string.byte(c))
   
end)
s = string.gsub(s, " ", "+")
return s
end
function
encode(t)
local s = "" for k, v in pairs(t) do
   
s = s .. "&" .. escape(k) .. "=" .. escape(v)
end
   
return string.sub(s, 2)
end
t = { name = "al", query = "a+b = c", q = "yes or no" }
print(encode(t))

3.IO库

--简单i/o模式
--Write函数与print函数区别:write不附加任何字符到输出,如:制表符/换行符
--write使用当前输出文件,print使用标准输出,会自动调用tostring函数
io.write("sin (3) = ", math.sin(3), "\n")

io.write(string.format("sin (3) = %.4f\n", math.sin(3)))

print("hello", "Lua"); print("Hi")

io.write("hello", "Lua"); io.write("Hi", "\n")

--read函数
--*all 从当前位置读取整个文件 ,注意返回值,当文件没有内容 则返回空
--*line 读取下一行,不包含最后换行符,当达到文件末尾返回nil io.read()默认方式
--*number 从串中转换出一个数组
--num 读取n个字符到串...
--读取3个数字,返回最大值
while true do local n1, n2, n3 = io.read("*number", "*number", "*number")
if not n1 then break end
   
print(math.max(n1, n2, n3))
end
--将数值n作为read函数的参数:返回n个字符当串 或者nil  io.read(0)测试是否返回到文件末尾 是返回空串,否返回nil
local size = 2 ^ 13
while true do local block = io.read(size) if not block then break end io.write(block) end

--完全i/o模式:对输入输出做更全面对控制 io.open(); 模式字符串:r w a

print(io.open("/etc/passwd", "w"))
local f = assert(io.open(filename, mode))

local f = assert(io.open(filename, "r"))
local t = f:read("*all") f:close()
--序列化
o = ''
--风险:出现特殊字符破坏程序结果
io.write("'", o, "'")

--风险:注入代码 o= [[ ]]..os.execute('rm *')..[[ ]]
io.write("[[", o, "]]")

function serialize(o)
if type(o) == "number" then
       
io.write(o)
elseif type(o) == "string" then
       
io.write(string.format("%q", o))
elseif type(o) == "table" then
       
io.write("{\n")
for k, v in pairs(o) do
           
io.write(" ", k, " = ")
serialize(v)
io.write(",\n")
end
       
io.write("}\n")
else
       
error("cannot serialize a " .. type(o))
end
end

i = 1 ;
serialize(i .. "\n")
serialize("ab'/cd123|?" .. "\n")

t = {a=1,b='b',c="aa\\bb/'" }
t2 = {["name"]="tom",["age"]=10,["birthday"]="1988-08-04 \00"}
serialize(t)
serialize(t2)

注:

1.序列化:将数据转换为字节流或字符流保存或在网络上传送

2.buff = buff .. line .. "\n" 字符串.. 连接效率比较低,并且比较占用内存 (原理:创建一个新的字符串变量保存结果并连接新字符串)solution:使用io.read(*all)将整个文件读入内存 不建议使用io.readline()

环境.异常.错误信息

1.环境

--Lua将环境本身存储在一个全局变量_G 以变量名为索引的表
for n in pairs(_G) do print(n) end

--获取全局变量,并赋值给其他变量
p = _G["print"]
p("print 赋值 f", type(p))

--访问非全局变量均无效
setmetatable(_G, {
__newindex = function(_, n)
error("attempt to write to undeclared variable " .. n, 2)
end,
   
__index = function(_, n)
error("attempt to read undeclared variable " .. n, 2)
end,
})
a = 1
--使用rawset,可以绕过metamethod
function declare(name, initval)
rawset(_G, name, initval or false)
end
declare("b")
b = 1;
print(b)

2.异常&错误信息

a.assert

内置函数assert(param,exp) param为假,执行exp

assert(nil,"msg:invoid value")

b.error函数显示抛出错误

函数异常处理方式:

  • 抛出错误异常

  • 返回错误代码

if not n then
   
error("aaaa:invalid input")
end

c.pcall在保护模式下调用函数,

  • 正常情况返回true和函数返回值

  • 异常情况返回nil和错误信息

--pcall function
local status, err = pcall(function () error({code=121}) end)
print(status,err.code)

function right()
return "res: hello lua!"
end

function
wrong()
error({code=121,msg="wrong"},2)
end

local
status, res = pcall(right)
print(status,res)

local status, res = pcall(wrong)
print(status,res.code .. res.msg)

d.xpcall

pcall返回时已经释放错误堆栈信息,可以通过xpcall获取[包含2个参数:调用函数和错误处理函数]

在释放错误堆栈信息前调用错误处理函数搜集错误异常信息

function foo (str)
if type(str) ~= "string" then
       
error("string expected:", 2)
end
end

--xpcall :第一个参数:调用函数,第二个参数:错误处理函数
function trace()
print("错误处理函数") ;

end
local
status, res = xpcall(foo("1"),trace)
print(status)

参考链接:

  • http://www.lua.org/home.html

  • 书籍:programming in lua

以上是关于笔记 | Programming in Lua的主要内容,如果未能解决你的问题,请参考以下文章

Programming In Lua 第一章

Programming In Lua 第八章

Programming In Lua 第三章

Programming In Lua 第四章

Programming In Lua 第五章

Programming In Lua 第十章