1、标准库
math库
----------------三角函数库----------------
--都是以弧度为单位
math.sin
math.cos
math.tan
math.asin
math.acos
math.pi = 3.1415926
math.deg --弧度 -> 度
math.rad --度 -> 弧度
----------------幂指函数库----------------
math.exp
math.log
math.log10
----------------舍入函数----------------
math.floor
math.ceil
math.max
math.min
----------------随机数函数----------------
math.random() -- [0, 1)
math.random(n) -- [1, n]
math.random(a, b) -- [a, b]
math.randomseed(seed) --随机数发生器,seed:number,相同seed,随机序列相同。
table库
----------------数组操作----------------
table.setn(t, len) -- 设置数组t长度为len,t.n = len
-- 这个功能是为了兼容旧版本
table.getn(t) -- 数组长度t.n,如果n不存在,则遍历。
table.insert(t, pos, v) -- 在t的pos位置插入v,后面元素后移
table.insert(t, v) -- 在t的尾部插入
table.remove(t, pos, v) -- 删除pos位置元素,后面元素前移
table.remove(t) -- 删除最后一个
-- 几百个元素的插入删除,效率没有问题。
table.sort(t, function(left, right) --对数组t排序,
-- left: 数组元素
-- right: left相邻右边的元素
-- return true: left 和 right 不交换位置,反之交换位置。
-- return a < b: 默认排序,如为number,则从小到大排序。
end)
local a = { 1, 2, 3, nil, 5, 6}
print(table.getn(a)) --> 3
-------------按指定key排序方法,遍历数组-------------
local a = {
luaH_set = 10,
luaH_get = 24,
luaH_present = 48,
}
local function pairsByKeys(t, key_order)
local keys = {}
for k in pairs(t) do table.insert(keys, k) end
table.sort(keys, key_order)
local var_control = 0
return function(t)
var_control = var_control + 1
if keys[var_control] ~= nil then
return keys[var_control], t[keys[var_control]]
else
return nil
end
end, t
end
for k in pairsByKeys(t) do
print(k) --> luaH_get、luaH_present、luaH_set
end
string库
常见函数
-- 位置规则以下通用:
-- 位置<0,则从尾部往头部计算位置,
-- -1最后一个字符位置,-2倒数第二的位置
string.len(str) --字符串长度
string.rep(str, n) --str重复n次的串
string.lower(str) --切换成小写
string.upper(str) --切换成大写
string.sub(str, i, j) --返回一个str串的子串,位置:i~j
--i,j<0,从尾部计数,-1是最后一个位置。
--j:默认-1,
string.sub(str, i, -1) -- 等价
string.sub(str, i) -- 等价
string.char(...) -- 返回字符串:由每个数字转成字符拼接而成。
string.byte(str, i) -- pos位置字符转成整数,pos默认1
string.byte(str, 1) -- 等价
string.byte(str) -- 等价
string.format(str, ...) --和C printf几乎一样
--------------例子--------------
local s = string.rep("a", 2^20) --1MB的串,测试需要
string.sub(s, 2, -2) -- 注意s并没有改变
s = string.sub(s, 2, -2) -- s才是截取的字符串,新手很容易出错
--%x:十六进制,%d:十进制,%o:八进制,%f:浮点数
string.format("pi = %.4f", PI) --> pi = 3.1416
d = 5; m = 11; y = 1990
string.format("%02d/%02d/%04d", d, m, y) --> 05/11/1990
tag, title = "h1", "a title"
string.format("<%s>%s</%s>", tag, title, tag) --> <h1>a title</h1>
模式匹配函数
string.find(字符串查找)
string.gsub(全局字符串替换)
string.gfind(全局字符串查找)
string.gmatch(返回查找到字符串的迭代器)
string.find
-- 查找str中匹配pattern的字串起始位置和捕获(如果有)
-- str: 目标字符串
-- pattern: 字符串、模式
-- start: 从str的start位置开始搜索,可以用于查找全部。
-- plain: bool值,false表示使用模式匹配,true表示普通的字符串匹配。默认false
-- a, b: 匹配字符串的首尾位置
-- ...: 返回捕获值,如果有。
a, b, ... = string.find(str, pattern, start, plain)
local s = "am+df"
print(string.find(s, "m+", 1, false)) -- 2 2
print(string.find(s, "m+", 1, true)) -- 2 3
print(string.find(s, "(m+)", 1, false)) -- 2 2 m
print(string.find(s, "((m+))", 1)) -- 2 2 m m
-- 先最外层捕获
print(string.find(s, "(((m+)))", 1)) -- 2 2 m m m
-- ()当前字符串匹配进行到的位置
print(string.find(s, "()(m+)()", 1)) -- 2 2 2 m 3
string.gsub
-- 查找str中匹配pattern的所有字符串,并用replace产生的字串替换
-- str: 目标字符串
-- pattern: 字符串、模式
-- replace: 字符串时,这个好理解
-- table时,key为第一个捕获的串,没有显示捕获,就是整个匹配到的,没有查询到不做替换
-- 函数时,function(...)end,...为所有的捕获(按顺序),返回值必须是数字或字符串
-- time: 最大替换次数
-- s: 替换后的字符串(副本)
-- count: 替换次数
s, count = string.gsub(str, pattern, replace, time)
local s = "am+dmf"
--%d表示第d个捕获,1<d<=9,%0表示匹配到的整个串。
print(string.gsub(s, "()(m+)", "%1")) -- a2+d5f 2
print(string.gsub(s, "()(m+)", "%2")) -- am+dmf 2
print(string.gsub(s, "()(m+)", "%3")) -- error: invalid capture index
print(string.gsub(s, "()(m+)", "%0%0%0")) -- ammm+dmmmf 2
function f1(...)
print(...) -- 2 m
-- 5 m
return "hh"
end
function f2()
return { 123 }
end
print(string.gsub(s, "()(m+)", f1)) -- ahh+dhhf 2
print(string.gsub(s, "()(m+)", f2)) -- error : invalid replacement value ( a table )
string.match
-- 查找str中匹配pattern的第一个捕获,默认是整个pattern匹配的
-- str: 目标字符串
-- pattern: 字符串、模式
-- start: 匹配起始位置
-- capture: 第一个捕获
capture = string.match(str, pattern, start)
string.gmatch
老版本叫string.gfind
-- 返回str匹配pattern的迭代器,一般用于遍历匹配全部。
-- str: 目标字符串
-- pattern: 字符串、模式
-- iter: 一个迭代器
iter = string.gmatch(str, pattern)
------------------------例子------------------------
t, s = {},"from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
t[ k ]=v
end
for k, v in pairs(t) do print(k, v) end
模式匹配pattern
出于程序大小考虑,Lua采用的不是通用POSIX规范的正则表达式(regexp)。
字符类
字符类指可以匹配一个特定字符集合内任何字符的模式项,下面是Lua支持的所有字符类:
. | 任意字符 | %a | 字母 |
---|---|---|---|
%c | 控制字符 | %d | 数字 |
%p | 标点字符 | %l | 小写字母 |
%s | 空白符 | %u | 大写字母 |
%w | 字母和数字 | %z | 代表0的字符 |
大写是小写的补集 |
特殊含义字符
( ) . % + - * ? [ ^ $
%后面跟任意一个非字母和数字的字符,都代表了这个字符本身,包括上面那些特殊字符以及任何标点符号都可以用这个方式来表达。
[…]方括号,自定义字符类,[%w_]匹配字母数字和下划线,[01]匹配二进制数字,可以使用范围表示字符集合,如[0-9]等价于%d,%x等价于[0-9a-fA-F]
[^…],^在方括号中表示补集。’[^0-7]’ 匹配任何不是八进制数字的字符。
以 ‘^’ 开头的模式只匹配目标串的开始部分。
以 ‘$’ 结尾的模式只匹配目标串的结尾部分。
‘%b’ 用来匹配对称的字符。如’%b()’ 匹配以 ‘(‘ 开始,以 ‘)’ 结束的字符串,包括()。常见的有’%b()’ ,’%b[]’,’%b%{%}’ 和 ‘%b<>’
尽量使用%l表示小写字母更简单更高效,而不是[a-z],字符类依赖于本地环境。
修饰符
- 匹配前一字符1次或多次,最长匹配
* 匹配前一字符0次或多次
- 匹配前一字符0次或多次,最短匹配
? 匹配前一字符0次或1次
一般情况下,Lua中的模式匹配效率是不错的,尽可能的更明确的模式描述。一个限制宽松的模式比限制严格的模式可能慢很多,永远不要写一个以 ‘-‘ 开头或者结尾的模式,因为它将匹配空串,一个包含 ‘.*’ 的模式是需要注意的,因为这个结构可能会比你预算的扩展的要多。
常见的模式匹配
-- 查找一个文本中行字符大于70个的行,也就是匹配一个非换行符之前有70个字符的行。
pattern = string.gsub("[^\n]", 70) .. "[^\n]*"
-- 字符串s1转化为s2,而不关心其中的特殊字符
s1 = string.gsub(s1, "(%W)", "%%%1")
s2 = string.gsub(s2, "%%", "%%%%")
捕获Captures
一种机制,使用模式串的一部分匹配目标串的一部分。将你想捕获的模式用()括起来,就指定了一个capture。
pair = "name = Anna"
a, b, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)")
print(key, value) --> name Anna
-- a、b是匹配字符串的索引下标
2、I/O库
注意一个小问题,DOS和UNIX系统的文本文件格式不同,DOS是回车换行符\r\n,UNIX是换行符\n,请注意替换区分。
简单I/O模式
非句柄式操作文件。
-- 设置当前标准输入(stdin),如文件,默认文本模式
-- filename_or_handle: 可以是文件名或者完全I/O模式的文件句柄
io.input(filename_or_handle)
io.output(a) --标准输出,同上用法。
io.flush() --清空输出缓冲,即所有write操作立即生效。
print --始终使用stdout
io.read(arg) --从标准输入读入一行
io.read("*all") --从当前位置,读取整个文件,刚好在文件尾或空文件,返回空串
io.read("*line") --从当前位置,读取下一行,没有换行符,没有返回nil
io.read() --同上等价
io.lines() --逐行读取迭代器,用于泛型for
io.read("*number") --从当前位置,读取1个可识别的数字,返回number,找不到返回nil
--文件内数字字符用空格等隔开,效率大增
io.read("*number","*number") --读取2个可识别的数字
io.read("*number","*number","*number") --读取3个可识别的数字
io.read(10) --读取最多10个字符(字节)
--可以看到这些参数可以组合的
--8KB为单元读取,避免切割行
--lines: 8KB字节内容
--rest: 如何切割了行,就返回行剩余部分
--lines,rest拼接起来就是行结尾的内容。
local lines, rest = io.read(2^13, "*line")
io.write(...) --向标准输出输出
io.write(string.format(...)) -- 常用方法
---------------例子---------------
a, b, c = "a", "b", "c"
io.write(a .. b .. c) --杜绝此类写法
io.write(a, b, c) --以此代替
local cell_size = 2^13 -- 高效复制文件
while true do
local block = io.read(cell_size)
if not block then break end
io.write(block)
end
完全I/O模式
通过句柄操作文件,类似C的FILE*
-- 获取filename的句柄,俗称打开文件
-- 注意可能失败返回nil,做好考虑
-- filename : 文件名
-- mode : 打开模式。
-- r:读模式,默认模式
-- w:写模式,覆盖
-- a:追加模式
-- b:二进制模式,可附加在后面
-- return : handle,文件句柄,打开成功
-- errorinfo,打开失败信息,handle为nil
local handle = io.open(filename, mode)
-- 调整当前文件的存取位置,文件就像一个线性表,存取位置就是“栅栏”
-- whence: offset的偏移方式,默认为"cur"
-- set:从文件头便宜offset个字节
-- cur:从当前位置offset个字节
-- end:从文件尾往前偏移offset个字节
-- offset: 偏移字节数,默认0
-- return: 新的存取位置,
local pos = handle.seek(whence, offset)
io.stderr --预定义句柄,可直接使用,别特么自己close了
io.stdin --预定义句柄
io.stdout --预定义句柄
local f = assert(io.open(filename, mode)) --常见使用方式
f:read("*all") --操作方式和简单I/O一样,但是注意用冒号:
f:close() --切记别忘了关闭
3、os库
文件管理,系统时钟等等与操作系统相关信息,只提供ANSI C标准内的,没有目录管理,套接字。
os.clock() --当前CPU时钟秒数,用来计算执行时间
os.exit() --程序终止执行
os.getenv(envname) --等到环境变量,如:
os.getenv("HOME") -- /home/lua,没有返回nil
os.execute(cmd) -- 如MS-DOS命令,和C的system函数等价
--设置程序所在区域
--category: 特性,默认all。
-- collate排序,控制字符的排列顺序
-- monetary货币
-- numeric数字,控制数字的格式
-- time控制时间的格式
-- all包含以上所以特性
os.setlocale(locale, category)
os.setlocale("ISO-8859-1", "collate")
-- a=nil时,返回当前距离某个特定时间的秒数
-- a~=nil时,返回距离时间表a的秒数
-- a:时间表,格式如下:
-- {
-- year=1970, month=1,
-- day=1, hour=0,
-- sec=1, isdst
-- }
-- isdst:是否夏令时间,一般都停用了
local seconds = os.time(a)
-- 和os.time互为“反函数”
-- 返回秒数seconds对应的时间表
-- format: 时间表格式
-- *t,返回下面temp格式
-- %*,根据标记为填充,格式化输出,看下面例子
-- seconds: 距离某一时间的秒数,默认当前时间秒数
-- return: 返回时间表
local temp = os.date(format, seconds)
--temp = {
-- year = 1998,
-- month = 9,
-- day = 16,
-- yday = 259, --一年中的第几天,1月1日为1
-- wday = 4, --星期天为1
-- hour = 23,
-- min = 48,
-- sec = 10,
-- isdst = false
--}
print(os.date("today is %A, in %B")) --> today is Tuesday, in May
print(os.date("%x", 906000490)) --> 09/16/1998
-- 全部的标记位如下:
%a abbreviated weekday name (e.g., Wed)
%A full weekday name (e.g., Wednesday)
%b abbreviated month name (e.g., Sep)
%B full month name (e.g., September)
%c date and time (e.g., 09/16/98 23:48:10)
%d day of the month (16) [01-31]
%H hour, using a 24-hour clock (23) [00-23]
%I hour, using a 12-hour clock (11) [01-12]
%M minute (48) [00-59]
%m month (09) [01-12]
%p either "am" or "pm" (pm)
%S second (10) [00-61]
%w weekday (3) [0-6 = Sunday-Saturday]
%x date (e.g., 09/16/98)
%X time (e.g., 23:48:10)
%Y full year (1998)
%y two-digit year (98) [00-99]
%% the character '%'
4、debug库
提供设计调试器的接口,C API实现。
表debug包含全部函数。分两类函数:
自省函数:introspective,获取运行程序当前状态,如活动函数栈、当前执行代码的行号、本地变量的名和值。
跟踪函数:hooks,到达某一事件时,回调。
栈级别(stack level):
调用debug库的函数级别为1,设为func
调用func的函数级别为2,以此类推
自省:debug.info
-- arg: number: 返回n级栈的活动函数的信息数据,n过大,返回nil
-- function: 函数变量。
-- conf: string,需要返回哪些信息的配置。mask字符串,如"nfl"
-- "n",返回name、namewhat
-- "f",返回func
-- "S",返回source, short_src, what, and linedefined
-- "l",返回currentline
-- "u",返回nup
-- return: 函数信息表
local info = debug.getinfo(n, conf)
info = {
-- source 函数被定义的地方,字符串(loadstring)或者文件名
-- short_src 记录一些有用的错误信息, 最多60byte
-- linedefined 函数在source中被定义的行号
-- what 标明函数类型。
-- lua: lua函数
-- C: C函数
-- main: 主chunk。
-- name 函数的合理名称。
-- namewhat 上一个字段代表的含义。"global"、"local"、"method"、"field",
-- 或者 ""(空字符串)。空字符串意味着Lua没有找到这个函数名。
-- nups 函数的upvalues的个数。
-- func 函数本身;详细情况看后面。
-- currentline getinfo(n)时,才有的字段。
}
-- func_or_stacklvl是C函数时,what, name, namewhat可用。
debug.getinfo(1) -- 返回当前所在函数的栈信息
debug.getinfo(0) -- debug.getinfo本身
debug.traceback()
-- 获取活动栈中活动函数的局部变量
-- stack_lvl: 栈级别,1:调用debug.getlocal所在函数。
-- var: 要获取的变量索引,按声明顺序,见下例
-- var_name: 变量名称
-- var_val: 变量值
local var_name, var_val = debug.getlocal(stack_lvl, var)
-- 修改活动栈中活动函数的局部变量
-- stack_lvl: 栈级别,1:调用debug.getlocal所在函数。
-- var: 要获取的变量索引,按声明顺序,见下例
-- new_val: 变量新值
-- var_name: 变量名称
local var_name = debug.setlocal(stack_lvl, var, new_val)
-- 获取/设置函数的upvalue
-- func: 函数,为啥没有栈级别了?函数不在运行太也有闭包
-- upval_index: upvalue的索引。按被引用顺序
-- new_val: upvalue新值
-- var_name: upvalue名称
local upval_name = debug.getupvalue(func, upval_index)
local upval_name = debug.setupvalue(func, upval_index, new_val)
-----------------debug.getlocal 例子-----------------
function foo (a,b) -- a:索引为1,b:索引为2
local x -- 索引为3
do local c = a - b end
local a = 1 -- 索引为4,存储的是索引值
while true do
local name, value = debug.getlocal(1, a)
if not name then break end
print(name, value)
a = a + 1
end
end
foo(10, 20)
--结果为:
--a 10
--b 20
--x nil
--a 4
-----------------debug.getupvalue 例子-----------------
function getvarvalue (name) --查找变量名为name的变量值
local value, found
local i = 1
while true do
local n, v = debug.getlocal(2, i) --1、先找局部变量
if not n then break end
if n == name then
value = v
found = true
end
i = i + 1
end
if found then return value end
-- try upvalues
local func = debug.getinfo(2).func
i = 1
while true do
local n, v = debug.getupvalue(func, i) -- 2、再找upvalue是否存在
if not n then break end
if n == name then return v end
i = i + 1
end
-- not found; get global
return getfenv(func)[name] --3、再看调用getvarvalue的函数环境中是否有这个全局变量。
end
hook函数
--当触发event事件,回调callback,参数arg
--callback: 回调函数function(event, arg)end
--event: 触发回调事件,mask字符串
-- call/c: 调用函数时
-- return/r: 函数返回时
-- line/l: 执行代码新行时
-- count: 运行arg条指令时,
debug.sethook(callback, event, arg) -- 注册
debug.sethook() --注销
-----------------例子-----------------
function trace (event, line)
local s = debug.getinfo(2).short_src --文件信息等
print(s .. ":" .. line)
end
debug.sethook(trace, "l")
Profiling 统计
------------记录每个函数被调用次数------------
local function hook ()
local f = debug.getinfo(2, "f").func --获得调用hook的函数chunk
if Counters[f] == nil then --第一次调用函数f
Counters[f] = 1
Names[f] = debug.getinfo(2, "Sn") --key:函数,value:函数信息表
else
Counters[f] = Counters[f] + 1 --调用次数+1
end
end
function getname (func) --函数的函数名
local n = Names[func]
if n.what == "C" then --是C函数
return n.name
end
local loc = string.format("[%s]:%s",
n.short_src, n.linedefined) --[函数所在文件名]函数所在行数
if n.namewhat ~= "" then --找到函数名
return string.format("%s (%s)", loc, n.name) --[文件名]行数 函数名
else --没找到函数名
return string.format("%s", loc) --行数
end
end
-- prompt> lua profiler main-prog
local f = assert(loadfile(arg[1])) --main-prog的chunk
debug.sethook(hook, "c") --有函数调用时触发
f() --运行main-prog
debug.sethook() -- 关闭hook
for func, count in pairs(Counters) do
print(getname(func), count) --函数基本信息,函数调用次数
end
-----------------输出示例-----------------
[markov.lua]:4 884723
write 10000
[markov.lua]:0 (f) 1
read 31103
sub 884722
[markov.lua]:1 (allwords) 1
[markov.lua]:20 (prefix) 894723
find 915824
[markov.lua]:26 (insert) 884723
random 10000
sethook 1
insert 884723