Lua 基本概述
Lua
是一个小巧的脚本语言
。
是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组
,由 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo
所组成并于 1993
年开发。
设计目的
是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能
Lua 不适合作为开发独立应用程序的语言
Lua
脚本可以很容易的被 C/C++
代码调用,也可以反过来调用 C/C++ 的函数,这使得 Lua 在应用程序中可以被广泛应用。
不仅仅作为扩展脚本
,也可以作为普通的配置文件
,代替 XML、ini
等文件格式,并且更容易理解和维护。
标准 Lua 5.1 解释器
由标准 C
编写而成,代码简洁优美,几乎在所有操作系统和平台
上都可以编译和运行;
一个完整的标准 `Lua 5.1` 解释器不足 `200KB`
推荐使用的 LuaJIT 2
的代码大小也只有不足 500KB
,同时也支持大部分常见的体系结构。
在目前所有脚本语言引擎中,LuaJIT 2
实现的速度应该算是最快的之一。
这一切都决定了 Lua
是作为嵌入式脚本
的最佳选择。
Lua
语言的各个版本是不相兼容的。
Lua 环境搭建
windows
OpenResty
从1.9.3.2
开始对外同时公布维护了 Windows
版本,
其中直接包含了编译好的最新版本 LuaJIT
linux
# wget http://luajit.org/download/LuaJIT-2.1.0-beta1.tar.gz
# tar -xvf LuaJIT-2.1.0-beta1.tar.gz
# cd LuaJIT-2.1.0-beta1
# make
# sudo make install
# 由于LuaJIT 2.1 目前还是beta版本,所以在make install后,并没有进行luajit的符号连接,
# 可以执行下面的指令将luajit-2.1.0-beta1和luajit进行软连接,从而可以直接使用luajit命令
ln -sf luajit-2.1.0-beta1 /usr/local/bin/luajit
# 验证 LuaJIT 是否安装成功
# luajit -v
LuaJIT 2.1.0-beta1 -- Copyright (C) 2005-2015 Mike Pall.
http://luajit.org/
编辑器 JetBrains
- 安装
emmylua
插件
使用JetBrains
的编辑器, File->Settings->Plugins
安装 emmylua
插件
pycharm phpstorm都可以
Lua语法
数据类型
函数 type
能够返回一个值或一个变量所属的类型。
print(type("hello world")) -->output:string
print(type(print)) -->output:function
print(type(true)) -->output:boolean
print(type(360.0)) -->output:number
print(type(nil)) -->output:nil
nil(空)
nil
是一种类型,Lua
将 nil 用于表示“无效值”
。
一个变量在第一次赋值前的默认值是 nil
将 nil 赋予给一个全局变量就等同于删除它
local num
print(num) -->output:nil
num = 100
print(num) -->output:100
OpenResty
的 Lua 接口还提供了一种特殊的空值,即 ngx.null
,
用来表示不同于 nil 的“空值”
。
boolean(布尔)
布尔类型,可选值 true/false;
Lua 中 nil 和 false 为“假”
,
其它所有值均为“真”。
比如 0 和空字符串就是“真”
;
C 或者 Perl 程序员或许会对此感到惊讶。
local a = true
local b = 0
local c = nil
if a then
print("a") -->output:a
else
print("not a") --这个没有执行
end
if b then
print("b") -->output:b
else
print("not b") --这个没有执行
end
if c then
print("c") --这个没有执行
else
print("not c") -->output:not c
end
number(数字)
Number 类型
用于表示实数
,和 C/C++ 里面的 double
类型很类似。
可以使用数学函数 math.floor(向下取整)
和 math.ceil(向上取整)
进行取整操作。
local order = 3.99
local score = 98.01
print(math.floor(order)) -->output:3
print(math.ceil(score)) -->output:99
一般地,Lua
的 number 类型
就是用双精度浮点数
来实现的。
LuaJIT
支持所谓的“dual-number”(双数)模式
,
即 LuaJIT
会根据上下文用整型
来存储整数,而用双精度浮点数
来存放浮点数。
print(9223372036854775807LL - 1) -- 9223372036854775806
string 字符串
Lua
的字符串是不可改变
的值
Lua
中有三种方式表示字符串:
1 使用一对匹配的单引号。例:'hello'。
2 使用一对匹配的双引号。例:"abclua"。
3 字符串还可以用一种长括号(即[[ ]])括起来的方式定义
把两个正的方括号(即[[)间插入 n 个等号定义为第 n 级正长括号。
就是说,0 级正的长括号写作 [[ ,一级正的长括号写作 [=[,如此等等
local str1 = 'hello world'
local str2 = "hello lua"
local str3 = [["add\name",'hello']]
local str4 = [=[string have a [[]].]=]
print(str1) -->output:hello world
print(str2) -->output:hello lua
print(str3) -->output:"add\name",'hello'
print(str4) -->output:string have a [[]].
在 Lua
实现中,Lua 字符串
一般都会经历一个“内化”(intern)
的过程,
即两个完全一样的 Lua 字符串
在 Lua 虚拟机中
只会存储一份。
每一个 Lua 字符串
在创建时都会插入到 Lua 虚拟机内部的一个全局的哈希表
中
- 创建相同的 Lua 字符串并不会引入新的动态内存分配操作,所以相对便宜(但仍有全局哈希表查询的开销),
- 内容相同的 Lua 字符串不会占用多份存储空间,
- 已经创建好的 Lua 字符串之间进行相等性比较时是 O(1) 时间度的开销,而不是通常见到的 O(n).
table 表 (关联数组)
在 Lua 中,数组下标从 1 开始
计数。
官方解释:Lua lists have a base index of 1 because it was thought to be most friendly for non-programmers, as it makes indices correspond to ordinal element positions.
local corp = {
web = "www.google.com", --索引为字符串,key = "web",
-- value = "www.google.com"
telephone = "12345678", --索引为字符串
staff = {"Jack", "Scott", "Gary"}, --索引为字符串,值也是一个表
100876, --相当于 [1] = 100876,此时索引为数字
-- key = 1, value = 100876
100191, --相当于 [2] = 100191,此时索引为数字
[10] = 360, --直接把数字索引给出
["city"] = "Beijing" --索引为字符串
}
print(corp.web) -->output:www.google.com
print(corp["telephone"]) -->output:12345678
print(corp[2]) -->output:100191
print(corp["city"]) -->output:"Beijing"
print(corp.staff[1]) -->output:Jack
print(corp["staff"][1]) -->output:Jack
print(corp[10]) -->output:360
function 函数
local function foo()
print("in the function")
--dosomething()
local x = 10
local y = 20
return x + y
end
local a = foo --把函数赋给变量
print(a())
--output:
--in the function
--30
---1 全局
function foo()
end
--- 等价于
foo = function ()
end
-- 2 局部
local function foo()
end
---等价于
local foo = function ()
end
表达式
算术运算符
算术运算符 |
说明 |
---|---|
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
^ |
指数 |
% |
取模 |
print(1 + 2) -->打印 3
print(5 / 10) -->打印 0.5。 这是Lua不同于c语言的
print(5.0 / 10) -->打印 0.5。 浮点数相除的结果是浮点数
-- print(10 / 0) -->注意除数不能为0,计算的结果会出错
print(2 ^ 10) -->打印 1024。 求2的10次方
local num = 1357
print(num % 2) -->打印 1
print((num % 2) == 1) -->打印 true。 判断num是否为奇数
print((num % 5) == 0) -->打印 false。判断num是否能被5整数
关系运算符
Lua
语言中“不等于”
运算符的写法为:~=
关系运算符 |
说明 |
---|---|
< |
小于 |
> |
大于 |
<= |
小于等于 |
>= |
大于等于 |
== |
等于 |
~= |
不等于 |
print(1 < 2) -->打印 true
print(1 == 2) -->打印 false
print(1 ~= 2) -->打印 true
local a, b = true, false
print(a == b) -->打印 false
--- table
local a = { x = 1, y = 0}
local b = { x = 1, y = 0}
if a == b then
print("a==b")
else
print("a~=b")
end
---output:
a~=b
由于 Lua 字符串
总是会被“内化”
即相同内容的字符串只会被保存一份
因此 Lua 字符串
之间的相等性比较
可以简化为其内部存储地址
的比较。
这意味着 Lua 字符串的相等性比较
总是为 O(1)
.
而在其他编程语言
中,字符串的相等性比较则通常为 O(n)
,即需要逐个字节(或按若干个连续字节)进行比较
逻辑运算符
逻辑运算符 |
说明 |
---|---|
and |
逻辑与 |
or |
逻辑或 |
not |
逻辑非 |
local c = nil
local d = 0
local e = 100
print(c and d) -->打印 nil
print(c and e) -->打印 nil
print(d and e) -->打印 100
print(c or d) -->打印 0
print(c or e) -->打印 100
print(not c) -->打印 true
print(not d) -->打印 false
所有逻辑操作符将 false 和 nil
视作假
,其他任何值
视作真
对于 and 和 or
,“短路求值”
对于 not
,永远只返回 true 或者 false
字符串连接
1) 连接两个字符串,可以使用操作符“..”
(两个点)
如果其任意一个操作数是数字的话,Lua 会将这个数字转换成字符串
print("Hello " .. "World") -->打印 Hello World
print(0 .. 1) -->打印 01
2) string 库函数 string.format
连接字符串
str1 = string.format("%s-%s","hello","world")
print(str1) -->打印 hello-world
str2 = string.format("%d-%s-%.2f",123,"world",1.21)
print(str2) -->打印 123-world-1.21
3) 推荐使用 table
和 table.concat()
来进行很多字符串的拼接
由于 Lua 字符串本质上是只读的,因此字符串连接运算符几乎总会创建一个 新的(更大的)字符串。
这意味着如果有很多这样的连接操作(比如在循环中使用 .. 来拼接最终结果),则性能损耗会非常大。
local pieces = {}
for i, ele in ipairs(my_list) do
pieces[i] = my_list(ele)
end
local res = table.concat(pieces)
优先级
优先级 |
---|
^ |
not # - |
* / % |
+ - |
.. |
< > <= >= == ~= |
and |
or |
local a, b = 1, 2
local x, y = 3, 4
local i = 10
local res = 0
res = a + i < b/2 + 1 -->等价于res = (a + i) < ((b/2) + 1)
res = 5 + x^2*8 -->等价于res = 5 + ((x^2) * 8)
res = a < y and y <=x -->等价于res = (a < y) and (y <= x)
流程控制
if-else
score = 90
if score == 100 then
print("Very good!Your score is 100")
elseif score >= 60 then
print("Congratulations, you have passed it,your score greater or equal to 60")
--此处可以添加多个elseif
else
print("Sorry, you do not pass the exam! ")
end
while
x = 1
sum = 0
while x <= 5 do
sum = sum + x
x = x + 1
end
print(sum) -->output 15
repeat
执行 repeat 循环体
后,直到 until 的条件为真时
才结束
x = 10
repeat
x = x - 1
print(x)
until x <= 0
for
for 语句有两种形式:数字 for(numeric for)
和范型 for(generic for)
。
1) 数字 for(numeric for)
for var = begin, finish, step do
--body
end
for i = 1, 10, 2 do
print(i)
end
-- output:
1
3
5
7
9
--- 不想给循环设置上限的话,可以使用常量 math.huge:
for i = 1, math.huge do
if (0.3*i^3 - 20*i^2 - 500 >=0) then
print(i)
break
end
end
2) for 泛型 通过一个迭代器(ipairs
)
local days = {
"Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday","Sunday"
}
local revDays = {}
for k, v in pairs(days) do
revDays[v] = k
end
-- print value
for k,v in pairs(revDays) do
print("k:", k, " v:", v)
end
-- output:
k: Tuesday v: 2
k: Monday v: 1
k: Sunday v: 7
k: Thursday v: 4
k: Friday v: 5
k: Wednesday v: 3
k: Saturd
break, return, goto
-- 1 break
sum = 10
i = 1
while true do
sum = sum + 1
if sum > 100 then
break
end
i = i + 1
end
print("result = ".. sum .. "; with i = " .. i)
-- return
-- 将 return 放在一个 do ... end 代码块中
local function foo()
print("before")
do return end
print("after") -- 这一行语句永远不会执行到
end
local function foo()
print("before")
return
print("after")
end
s = foo()
print(s)
--before
--nil
local function foo()
print("before")
return
print("after")
end
s = foo()
print(s)
--before
--after
--nil
-- goto
-- 有了 goto,我们可以实现 continue 的功能
-- [自定义log结构快]
for i=1, 3 do
if i <= 2 then
print(i, "yes logged")
goto log
end
do return end
:: log ::
print("logged")
end
Lua 函数
Lua
使用关键字 function
定义函数
function function_name (arc) -- arc 表示参数列表,函数的参数列表可以为空
-- body
end
-- 等价于
function_name = function (arc)
-- body
end
-- 例子
local function max(a, b) --定义函数 max,用来求两个数的最大值,并返回
local temp --使用局部变量 temp,保存最大值
if(a > b) then
temp = a
else
temp = b
end
return temp --返回最大值
end
local m = max(-12, 20) --调用函数 max,找去 -12 和 20 中的最大值
print(m)
函数参数
-- 1) 按值传递
local function fun1(a, b) --两个形参,多余的实参被忽略掉
print(a, b)
end
local function fun2(a, b, c, d) --四个形参,没有被实参初始化的形参,用nil初始化
print(a, b, c, d)
end
local x = 1
local y = 2
local z = 3
fun1(x, y, z) -- z被函数fun1忽略掉了,参数变成 x, y
fun2(x, y, z) -- 后面自动加上一个nil,参数变成 x, y, z, nil
-- 2) 变长参数
-- LuaJIT 2 尚不能 JIT 编译这种变长参数的用法,只能解释执行。所以对性能敏感的代码,应当避免使用此种形式
local function func( ... ) -- 形参为 ... ,表示函数采用变长参数
local temp = {...} -- 访问的时候也要使用 ...
local ans = table.concat(temp, " ") -- 使用 table.concat 库函数对数
-- 组内容使用 " " 拼接成字符串。
print(ans)
end
func(1, 2) -- 传递了两个参数
func(1, 2, 3, 4) -- 传递了四个参数
-- 3) 具名参数
local function change(arg) -- change 函数,改变长方形的长和宽,使其各增长一倍
arg.width = arg.width * 2
arg.height = arg.height * 2
return arg
end
local rectangle = { width = 20, height = 15 }
print("before change:", "width =", rectangle.width,
"height =", rectangle.height)
rectangle = change(rectangle)
print("after change:", "width =", rectangle.width,
"height =", rectangle.height)
-- 4) 按引用传递
function change(arg) --change函数,改变长方形的长和宽,使其各增长一倍
arg.width = arg.width * 2 --表arg不是表rectangle的拷贝,他们是同一个表
arg.height = arg.height * 2
end -- 没有return语句了
local rectangle = { width = 20, height = 15 }
print("before change:", "width = ", rectangle.width,
" height = ", rectangle.height)
change(rectangle)
print("after change:", "width = ", rectangle.width,
" height =", rectangle.height)
函数的返回值
允许函数返回多个值
function init() --init 函数 返回两个值 1 和 "lua"
return 1, "lua"
end
x = init()
print(x)
x, y, z = init()
print(x, y, z)
--output
1
1 lua nil
-- 如果你确保只取函数返回值的第一个值,可以使用括号运算符
local function init()
return 1, "lua"
end
print((init()), 2) -->output 1 2
print(2, (init())) -->output 2 1
模块
Lua
提供了一个名为 require
的函数用来加载模块
在 Lua
中创建一个模块最简单的方法是:创建一个 table
,
并将所有需要导出的函数放入其中,最后返回这个 table
就可以了。
相当于将导出的函数作为 table 的一个字段,
在 Lua
中函数是第一类值,提供了天然的优势。
-- my.lua
local _M = {}
local function get_name()
return "Lucy"
end
function _M.greeting()
print("hello " .. get_name())
end
return _M
-- main.lua
local my_module = require("my")
my_module.greeting() -->output: hello Lucy
对于需要导出给外部使用的公共模块
,处于安全考虑,是要避免全局变量的出现
。
我们可以使用 lj-releng
或 luacheck
工具完成全局变量的检测
#### String 库
string.byte(s [, i [, j ]])
返回字符 s[i]、s[i + 1]、s[i + 2]、······、s[j] 所对应的 ASCII 码。
i 的默认值为 1,即第一个字节,j 的默认值为 i 。
示例代码
print(string.byte("abc", 1, 3))
print(string.byte("abc", 3)) -- 缺少第三个参数,第三个参数默认与第二个相同,此时为 3
print(string.byte("abc")) -- 缺少第二个和第三个参数,此时这两个参数都默认为 1
-->output
97 98 99
99
97
由于 string.byte 只返回整数,而并不像 string.sub 等函数那样(尝试)创建新的 Lua 字符串
因此使用 string.byte 来进行字符串相关的扫描和分析是最为高效的,尤其是在被 LuaJIT 2 所 JIT 编译之后。
string.char (...)
接收 0 个或更多的整数(整数范围:0~255),返回这些整数所对应的 ASCII 码字符组成的字符串。
当参数为空时,默认是一个 0。
print(string.char(96, 97, 98))
print(string.char()) -- 参数为空,默认是一个0,
-- 你可以用string.byte(string.char())测试一下
print(string.char(65, 66))
--> output
`ab
AB
此函数特别适合从具体的字节构造出二进制字符串。
这经常比使用 table.concat 函数和 .. 连接运算符更加高效。
string.upper(s)
接收一个字符串 s,返回一个把所有小写字母变成大写字母的字符串。
print(string.upper("Hello Lua")) -->output HELLO LUA
string.lower(s)
接收一个字符串 s,返回一个把所有大写字母变成小写字母的字符串。
print(string.lower("Hello Lua")) -->output hello lua
string.len(s)
接收一个字符串,返回它的长度。
print(string.len("hello lua")) -->output 9
print(#"hello lua") -->output 9
使用此函数是不推荐的。
应当总是使用 # 运算符来获取 Lua 字符串的长度。
由于 Lua 字符串的长度是专门存放的,并不需要像 C 字符串那样即时计算
,因此获取字符串长度的操作总是 O(1) 的时间复杂度。
string.find(s, p [, init [, plain]])
在 s 字符串中第一次匹配 p 字符串。
若匹配成功,则返回 p 字符串在 s 字符串中出现的开始位置和结束位置;
若匹配失败,则返回 nil。
第三个参数 init 默认为 1,并且可以为负整数,当 init 为负数时,
表示从 s 字符串的 string.len(s) + init + 1 索引处开始向后匹配字符串 p 。
第四个参数默认为 false,当其为 true 时,只会把 p 看成一个字符串对待。
local find = string.find
print(find("abc cba", "ab"))
print(find("abc cba", "ab", 2)) -- 从索引为2的位置开始匹配字符串:ab
print(find("abc cba", "ba", -1)) -- 从索引为7的位置开始匹配字符串:ba
print(find("abc cba", "ba", -3)) -- 从索引为5的位置开始匹配字符串:ba
print(find("abc cba", "(%a+)", 1)) -- 从索引为1处匹配最长连续且只含字母的字符串
print(find("abc cba", "(%a+)", 1, true)) --从索引为1的位置开始匹配字符串:(%a+)
-->output
1 2
nil
nil
6 7
1 3 abc
nil
对于 LuaJIT 这里有个性能优化点,对于 string.find 方法,当只有字符串查找匹配时,
是可以被 JIT 编译器优化的,有关 JIT 可以编译优化清单,大家可以参考 http://wiki.luajit.org/NYI,性能提升是非常明显的,通常是 100 倍量级。这里有个的例子,大家可以参考 https://groups.google.com/forum/m/#!topic/openresty-en/rwS88FGRsUI。
string.format(formatstring, ...)
按照格式化参数 formatstring,返回后面 ... 内容的格式化版本
编写格式化字符串的规则与标准 c 语言中 printf 函数的规则基本相同:
它由常规文本和指示组成,这些指示控制了每个参数应放到格式化结果的什么位置,
及如何放入它们。一个指示由字符 % 加上一个字母组成,这些字母指定了如何格式化参数,
例如 d 用于十进制数、x 用于十六进制数、o 用于八进制数、f 用于浮点数、s 用于字符串等
在字符 % 和字母之间可以再指定一些其他选项,用于控制格式的细节。
print(string.format("%.4f", 3.1415926)) -- 保留4位小数
print(string.format("%d %x %o", 31, 31, 31))-- 十进制数31转换成不同进制
d = 29; m = 7; y = 2015 -- 一行包含几个语句,用;分开
print(string.format("%s %02d/%02d/%d", "today is:", d, m, y))
-->output
3.1416
31 1f 37
today is: 29/07/2015
string.match(s, p [, init])
在字符串 s 中匹配(模式)字符串 p,若匹配成功,则返回目标字符串中与模式匹配的子串;否则返回 nil。
第三个参数 init 默认为 1,并且可以为负整数,当 init 为负数时,
表示从 s 字符串的 string.len(s) + init + 1 索引处开始向后匹配字符串 p。
print(string.match("hello lua", "lua"))
print(string.match("lua lua", "lua", 2)) --匹配后面那个lua
print(string.match("lua lua", "hello"))
print(string.match("today is 27/7/2015", "%d+/%d+/%d+"))
-->output
lua
lua
nil
27/7/2015
string.match 目前并不能被 JIT 编译,应 尽量 使用 ngx_lua 模块提供的 ngx.re.match 等接口。
string.gmatch(s, p)
返回一个迭代器函数,通过这个迭代器函数可以遍历到在字符串 s 中出现模式串 p 的所有地方。
s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do --匹配最长连续且只含字母的字符串
print(w)
end
-->output
hello
world
from
Lua
t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%a+)=(%a+)") do --匹配两个最长连续且只含字母的
t[k] = v --字符串,它们之间用等号连接
end
for k, v in pairs(t) do
print (k,v)
end
-->output
to Lua
from world
此函数目前并不能被 LuaJIT 所 JIT 编译,而只能被解释执行。
应 尽量 使用 ngx_lua 模块提供的 ngx.re.gmatch 等接口。
string.rep(s, n)
返回字符串 s 的 n 次拷贝。
print(string.rep("abc", 3)) --拷贝3次"abc"
-->output abcabcabc
string.sub(s, i [, j])
返回字符串 s 中,索引 i 到索引 j 之间的子字符串。
当 j 缺省时,默认为 -1,也就是字符串 s 的最后位置。
i 可以为负数。当索引 i 在字符串 s 的位置在索引 j 的后面时,将返回一个空字符串。
print(string.sub("Hello Lua", 4, 7))
print(string.sub("Hello Lua", 2))
print(string.sub("Hello Lua", 2, 1)) --看到返回什么了吗
print(string.sub("Hello Lua", -3, -1))
-->output
lo L
ello Lua
Lua
如果你只是想对字符串中的单个字节进行检查,使用 string.char 函数通常会更为高效。
string.gsub(s, p, r [, n])
将目标字符串 s 中所有的子串 p 替换成字符串 r。可选参数 n,表示限制替换次数。
返回值有两个,第一个是被替换后的字符串,第二个是替换了多少次。
print(string.gsub("Lua Lua Lua", "Lua", "hello"))
print(string.gsub("Lua Lua Lua", "Lua", "hello", 2)) --指明第四个参数
-->output
hello hello hello 3
hello hello Lua 2
此函数不能为 LuaJIT 所 JIT 编译,而只能被解释执行。
一般我们推荐使用 ngx_lua 模块提供的 ngx.re.gsub 函数。
string.reverse (s)
接收一个字符串 s,返回这个字符串的反转。
print(string.reverse("Hello Lua")) --> output: auL olleH
table 库
table.getn 获取长度
取长度操作符写作一元操作 #
。
字符串的长度是它的字节数(就是以一个字符一个字节计算的字符串长度)。
对于常规的数组,里面从 1 到 n 放着一些非空的值
的时候,它的长度就精确的为 n,
即最后一个值的下标。
如果数组有一个“空洞”
(就是说,nil 值被夹在非空值之间),
那么 #t
可能是指向任何一个是 nil 值
的前一个位置的下标
(就是说,任何一个 nil 值都有可能被当成数组的结束)。
这也就说明对于有“空洞”的情况,table 的长度存在一定的 不可确定性。
local tblTest1 = { 1, a = 2, 3 }
print("Test1 " .. table.getn(tblTest1))
local tblTest2 = { 1, nil }
print("Test2 " .. table.getn(tblTest2))
local tblTest3 = { 1, nil, 2 }
print("Test3 " .. table.getn(tblTest3))
local tblTest4 = { 1, nil, 2, nil }
print("Test4 " .. table.getn(tblTest4))
local tblTest5 = { 1, nil, 2, nil, 3, nil }
print("Test5 " .. table.getn(tblTest5))
local tblTest6 = { 1, nil, 2, nil, 3, nil, 4, nil }
print("Test6 " .. table.getn(tblTest6))
我们使用 Lua 5.1 和 LuaJIT 2.1 分别执行这个用例,结果如下:
# lua test.lua
Test1 2
Test2 1
Test3 3
Test4 1
Test5 3
Test6 1
# luajit test.lua
Test1 2
Test2 1
Test3 1
Test4 1
Test5 1
Test6 1
不要在 Lua
的 table
中使用 nil
值,如果一个元素要删除,直接 remove
,不要用 nil
去代替。
table.concat (table [, sep [, i [, j ] ] ])
对于元素是 string 或者 number 类型的表 table,返回 table[i]..sep..table[i+1] ··· sep..table[j] 连接成的字符串。
填充字符串 sep 默认为空白字符串。
起始索引位置 i 默认为 1,结束索引位置 j 默认是 table 的长度。
如果 i 大于 j,返回一个空字符串。
local a = {1, 3, 5, "hello" }
print(table.concat(a)) -- output: 135hello
print(table.concat(a, "|")) -- output: 1|3|5|hello
print(table.concat(a, " ", 4, 2)) -- output:
print(table.concat(a, " ", 2, 4)) -- output: 3 5 hello
table.insert (table, [pos ,] value)
在(数组型)表 table 的 pos 索引位置插入 value,其它元素向后移动到空的地方。
pos 的默认值是表的长度加一,即默认是插在表的最后。
local a = {1, 8} --a[1] = 1,a[2] = 8
table.insert(a, 1, 3) --在表索引为1处插入3
print(a[1], a[2], a[3])
table.insert(a, 10) --在表的最后插入10
print(a[1], a[2], a[3], a[4])
-->output
3 1 8
3 1 8 10
table.maxn (table)
返回(数组型)表 table 的最大索引编号;
如果此表没有正的索引编号,返回 0。
当长度省略时,此函数通常需要 O(n) 的时间复杂度来计算 table 的末尾。
因此用这个函数省略索引位置的调用形式来作 table 元素的末尾追加,是高代价操作。
local a = {}
a[-1] = 10
print(table.maxn(a))
a[5] = 10
print(table.maxn(a))
-->output
0
5
此函数的行为不同于 # 运算符,因为 # 可以返回数组中任意一个 nil 空洞或最后一个 nil 之前的元素索引。
当然,该函数的开销相比 # 运算符也会更大一些。
table.remove (table [, pos])
在表 table 中删除索引为 pos(pos 只能是 number 型)的元素,
并返回这个被删除的元素,它后面所有元素的索引值都会减一。
pos 的默认值是表的长度,即默认是删除表的最后一个元素。
local a = { 1, 2, 3, 4}
print(table.remove(a, 1)) --删除速索引为1的元素
print(a[1], a[2], a[3], a[4])
print(table.remove(a)) --删除最后一个元素
print(a[1], a[2], a[3], a[4])
-->output
1
2 3 4 nil
4
2 3 nil nil
table.sort (table [, comp])
按照给定的比较函数 comp 给表 table 排序,也就是从 table[1] 到 table[n],
这里 n 表示 table 的长度。
比较函数有两个参数,如果希望第一个参数排在第二个的前面,
就应该返回 true,否则返回 false。
如果比较函数 comp 没有给出,默认从小到大排序。
local function compare(x, y) --从大到小排序
return x > y --如果第一个参数大于第二个就返回true,否则返回false
end
local a = { 1, 7, 3, 4, 25}
table.sort(a) --默认从小到大排序
print(a[1], a[2], a[3], a[4], a[5])
table.sort(a, compare) --使用比较函数进行排序
print(a[1], a[2], a[3], a[4], a[5])
-->output
1 3 4 7 25
25 7 4 3 1
时间日期函数
在 Lua
中,函数 time、date 和 difftime
提供了所有的日期和时间功能
在 OpenResty
的世界里,不推荐使用这里的标准时间函数,因为这些函数通常会引发不止一个昂贵的系统调用
,
同时无法为 LuaJIT JIT
编译,对性能造成较大影响。
推荐使用 ngx_lua
模块提供的带缓存的时间接口
,如 ngx.today, ngx.time, ngx.utctime, ngx.localtime, ngx.now, ngx.http_time,以及 ngx.cookie_time
等
os.time ([table])
print(os.time()) -->output 1438243393
a = { year = 1970, month = 1, day = 1, hour = 8, min = 1 }
print(os.time(a)) -->output 60
os.difftime (t2, t1)
local day1 = { year = 2015, month = 7, day = 30 }
local t1 = os.time(day1)
local day2 = { year = 2015, month = 7, day = 31 }
local t2 = os.time(day2)
print(os.difftime(t2, t1)) -->output 86400
os.date ([format [, time]])
print(os.date("today is %A, in %B"))
print(os.date("now is %x %X"))
-->output
today is Thursday, in July
now is 07/30/15 17:39:22
数学计算库
函数名 |
函数功能 |
---|---|
math.rad(x) |
角度x转换成弧度 |
math.deg(x) |
弧度x转换成角度 |
math.max(x, ...) |
返回参数中值最大的那个数,参数必须是number型 |
math.min(x, ...) |
返回参数中值最小的那个数,参数必须是number型 |
math.random ([m [, n]]) |
不传入参数时,返回 一个在区间[0,1)内均匀分布的伪随机实数;只使用一个整数参数m时,返回一个在区间[1, m]内均匀分布的伪随机整数;使用两个整数参数时,返回一个在区间[m, n]内均匀分布的伪随机整数 |
math.randomseed (x) |
为伪随机数生成器设置一个种子x,相同的种子将会生成相同的数字序列 |
math.abs(x) |
返回x的绝对值 |
math.fmod(x, y) |
返回 x对y取余数 |
math.pow(x, y) |
返回x的y次方 |
math.sqrt(x) |
返回x的算术平方根 |
math.exp(x) |
返回自然数e的x次方 |
math.log(x) |
返回x的自然对数 |
math.log10(x) |
返回以10为底,x的对数 |
math.floor(x) |
返回最大且不大于x的整数 |
math.ceil(x) |
返回最小且不小于x的整数 |
math.pi |
圆周率 |
math.sin(x) |
求弧度x的正弦值 |
math.cos(x) |
求弧度x的余弦值 |
math.tan(x) |
求弧度x的正切值 |
math.asin(x) |
求x的反正弦值 |
math.acos(x) |
求x的反余弦值 |
math.atan(x) |
求x的反正切值 |
print(math.pi) -->output 3.1415926535898
print(math.rad(180)) -->output 3.1415926535898
print(math.deg(math.pi)) -->output 180
print(math.sin(1)) -->output 0.8414709848079
print(math.cos(math.pi)) -->output -1
print(math.tan(math.pi / 4)) -->output 1
print(math.atan(1)) -->output 0.78539816339745
print(math.asin(0)) -->output 0
print(math.max(-1, 2, 0, 3.6, 9.1)) -->output 9.1
print(math.min(-1, 2, 0, 3.6, 9.1)) -->output -1
print(math.fmod(10.1, 3)) -->output 1.1
print(math.sqrt(360)) -->output 18.97366596101
print(math.exp(1)) -->output 2.718281828459
print(math.log(10)) -->output 2.302585092994
print(math.log10(10)) -->output 1
print(math.floor(3.1415)) -->output 3
print(math.ceil(7.998)) -->output 8
-- 避免每次程序启动时得到的都是相同的伪随机数序列,通常是使用当前时间作为种子
math.randomseed (os.time()) --把100换成os.time()
print(math.random()) -->output 0.88369396038697
print(math.random(100)) -->output 66
print(math.random(100, 360)) -->output 228
文件操作
Lua
I/O 库提供两种不同的方式处理文件:隐式文件描述
,显式文件描述
这些文件 I/O
操作,在 OpenResty
的上下文中对事件循环是会产生阻塞效应
。
OpenResty
比较擅长的是高并发网络处理
,在这个环境中,任何文件的操作,都将阻塞其他并行执行的请求。
实际中的应用,在 OpenResty
项目中应尽可能让网络处理
部分, 文件 I/0 操作
部分相互独立,不要揉和在一起
隐式文件描述
设置一个默认的输入或输出文件
,然后在这个文件上进行所有的输入或输出操作
。
所有的操作函数由 io
表提供。
file = io.input("test1.txt") -- 使用 io.input() 函数打开文件
repeat
line = io.read() -- 逐行读取内容,文件结束时返回nil
if nil == line then
break
end
print(line)
until (false)
io.close(file) -- 关闭文件
--> output
my test file
hello
lua
file = io.open("test1.txt", "a+") -- 使用 io.open() 函数,以添加模式打开文件
io.output(file) -- 使用 io.output() 函数,设置默认输出文件
io.write("hello world") -- 使用 io.write() 函数,把内容写到文件
io.close(file)
显式文件描述
使用 file:XXX()
函数方式进行操作, 其中 file
为 io.open()
返回的文件句柄
file = io.open("test2.txt", "r") -- 使用 io.open() 函数,以只读模式打开文件
for line in file:lines() do -- 使用 file:lines() 函数逐行读取文件
print(line)
end
file:close()
-->output
my test2
hello lua
文件操作函数
io.open (filename [, mode])
按指定的模式 mode,打开一个文件名为 filename 的文件,
成功则返回文件句柄,失败则返回 nil 加错误信息。
模式 |
含义 | 文件不存在时 |
---|---|---|
"r" |
读模式 (默认) | 返回nil加错误信息 |
"w" |
写模式 | 创建文件 |
"a" |
添加模式 | 创建文件 |
"r+" |
更新模式,保存之前的数据 | 返回nil加错误信息 |
"w+" |
更新模式,清除之前的数据 | 创建文件 |
"a+" |
添加更新模式,保存之前的数据,在文件尾进行添加 | 创建文件 |
注意 "w"
和 "wb"
的区别
"w" 表示文本文件。某些文件系统(如 Linux 的文件系统)认为 0x0A 为文本文件的换行符,Windows 的文件系统认为 0x0D0A 为文本文件的换行符。为了兼容其他文件系统(如从 Linux 拷贝来的文件),Windows 的文件系统在写文件时,会在文件中 0x0A 的前面加上 0x0D。使用 "w",其属性要看所在的平台。
"wb" 表示二进制文件。文件系统会按纯粹的二进制格式进行写操作,因此也就不存在格式转换的问题。(Linux 文件系统下 "w" 和 "wb" 没有区别)
file:close ()
关闭文件。
注意:当文件句柄被垃圾收集后,文件将自动关闭。
句柄将变为一个不可预知的值。
io.close ([file])
关闭文件,和 file:close() 的作用相同。
没有参数 file 时,关闭默认输出文件。
file:flush ()
把写入缓冲区的所有数据写入到文件 file 中。
io.flush ()
相当于 file:flush(),把写入缓冲区的所有数据写入到默认输出文件。
io.input ([file])
当使用一个文件名调用时,打开这个文件(以文本模式),并设置文件句柄为默认输入文件; 当使用一个文件句柄调用时,设置此文件句柄为默认输入文件; 当不使用参数调用时,返回默认输入文件句柄。
file:lines ()
返回一个迭代函数, 每次调用将获得文件中的一行内容, 当到文件尾时,将返回 nil,但不关闭文件。
io.lines ([filename])
打开指定的文件 filename 为读模式并返回一个迭代函数, 每次调用将获得文件中的一行内容, 当到文件尾时,将返回 nil,并自动关闭文件。若不带参数时 io.lines() 等价于 io.input():lines() 读取默认输入设备的内容,结束时不关闭文件。
io.output ([file])
类似于 io.input,但操作在默认输出文件上。
file:read (...)
按指定的格式读取一个文件。按每个格式将返回一个字符串或数字, 如果不能正确读取将返回 nil,若没有指定格式将指默认按行方式进行读取。格式:
格式 |
含义 |
---|---|
"*n" |
读取一个数字 |
"*a" |
从当前位置读取整个文件。若当前位置为文件尾,则返回空字符串 |
"*l" |
读取下一行的内容。若为文件尾,则返回nil。(默认) |
number |
读取指定字节数的字符。若为文件尾,则返回nil。如果number为0,则返回空字符串,若为文件尾,则返回nil |
io.read (...)
相当于 io.input():read
io.type (obj)
检测 obj 是否一个可用的文件句柄。如果 obj 是一个打开的文件句柄,则返回 "file" 如果 obj 是一个已关闭的文件句柄,则返回 "closed file" 如果 obj 不是一个文件句柄,则返回 nil。
file:write (...)
把每一个参数的值写入文件。参数必须为字符串或数字,若要输出其它值,则需通过 tostring 或 string.format 进行转换。
io.write (...)
相当于 io.output():write。
file:seek ([whence] [, offset])
设置和获取当前文件位置,成功则返回最终的文件位置(按字节,相对于文件开头),失败则返回 nil 加错误信息。缺省时,whence 默认为 "cur",offset 默认为 0 。 参数 whence:
whence |
含义 |
---|---|
"set" |
文件开始 |
"cur" |
文件当前位置(默认) |
"end" |
文件结束 |
file:setvbuf (mode [, size])
设置输出文件的缓冲模式。
模式 |
含义 |
---|---|
"no" |
没有缓冲,即直接输出 |
"full" |
全缓冲,即当缓冲满后才进行输出操作(也可调用flush马上输出) |
"line" |
以行为单位,进行输出 |
最后两种模式,size
可以指定缓冲的大小(按字节)
忽略 size 将自动调整为最佳的大小。