目录

Lua

lua基本语法

Lua 学习起来非常简单,我们可以创建第一个 Lua 程序!

打印输出

1
    print("Hello World")

单行注释

两个减号是单行注释:

1
    --

多行注释

两个减号是单行注释:

1
2
3
4
--[[
 多行注释
 多行注释
 --]]

变量名称

Lua 标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。

最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。

Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。因此在 Lua 中 Nspirit 与 nspirit 是两个不同的标示符。以下列出了一些正确的变量名称:

案例
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal

  • 关键词不可以被用来定义变量名称
关键词
and break do else
elseif end false for
function if in local
nil not or repeat
return then true until
while goto

一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。

全局变量

在默认情况下,变量总是认为是全局的。

全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。

1
2
3
print(b) --> nil
b=10
print(b) --> 10

如果你想删除一个全局变量,只需要将变量赋值为nil。

1
2
b = nil
print(b)      --> nil

局部变量

只需要在变量名称前 加一个 local,该全局变量就会变为局部变量

1
  local x = 10 ;

局部变量值在作用域 范围内有效

  • 什么是域? 比如当前lua文件,或者当前function 内,还是不懂请进入QQ群内,讨论咨询。

数据类型

Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

类型 描述
nil 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean 包含两个值:false和true。
number 表示双精度类型的实浮点数
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序
table Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。
  • 实例
1
2
3
4
5
6
7
print(type("Hello world"))      --> string
print(type(10.4*3))             --> number
print(type(print))              --> function
print(type(type))               --> function
print(type(true))               --> boolean
print(type(nil))                --> nil
print(type(type(X)))            --> string

nil (空)

  • nil 类型表示一种没有任何有效值,它只有一个值 – nil,例如打印一个没有赋值的变量,便会输出一个 nil 值:
1
print(type(a))                --> nil

  • 对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,执行下面代码就知:
1
2
3
4
5
6
7
8
9
tab1 = { key1 = "val1", key2 = "val2", "val3" }
for k, v in pairs(tab1) do
    print(k .. " - " .. v)
end
 
tab1.key1 = nil
for k, v in pairs(tab1) do
    print(k .. " - " .. v)
end

  • nil 作比较时应该加上双引号 “:
1
2
3
4
5
type(X) --> nil

type(X)==nil --> nil

type(X)=="nil" --> true

boolean (布尔)

boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
print(type(true))
print(type(false))
print(type(nil))
 
if false or nil then
    print("至少有一个是 true")
else
    print("false 和 nil 都为 false")
end

if 0 then
    print("数字 0 是 true")
else
    print("数字 0 为 false")
end

以上代码执行结果

boolean

boolean

nil

false 和 nil 都为 false

数字 0 是 true


number (数字)

Lua 默认只有一种 number 类型 – double(双精度)类型(默认类型可以修改 luaconf.h 里的定义),以下几种写法都被看作是 number 类型:

1
2
3
4
5
6
print(type(2)) --> number
print(type(2.2)) --> number
print(type(0.2)) --> number
print(type(2e+1)) --> number
print(type(0.2e-1)) --> number
print(type(7.8263692594256e-06)) --> number

string (字符串)

  • 字符串由一对双引号或单引号来表示。
1
2
string1 = "this is string1"
string2 = 'this is string2'
  • 也可以用 2 个方括号 “[[]]” 来表示"一块"字符串。
1
2
3
4
5
6
7
8
9
html = [[
<html>
<head></head>
<body>
    <a href="http://www.nspirit.cn/">节点精灵</a>
</body>
</html>
]]
print(html)
  • 在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
print("2" + 6) -->8.0

print("2" + "6") -->8.0

print("2 + 6") --> 2 + 6

print("-2e2" * "6") --> -1200.0

print("error" + 1)
-->
-- stdin:1: attempt to perform arithmetic on a string value
-- stack traceback:
--        stdin:1: in main chunk
--        [C]: in ?

以上代码中"error” + 1执行报错了,字符串连接使用的是 .. ,如:

1
2
print("a" .. 'b') -->ab
print(157 .. 428) -->157428

  • 使用 # 来计算字符串的长度,放在字符串前面,如下实例:
1
2
3
len = "www.nspirit.cn"
print(#len) --> 14
print(#"www.nspirit.cn") -->14

table (表)

  • 在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据,直接初始化表:
1
2
3
4
5
-- 创建一个空的 table
local tbl1 = {}
 
-- 直接初始表
local tbl2 = {"apple", "pear", "orange", "grape"}

  • Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) do
    print(k .. " : " .. v)
end
-- 脚本执行结果为:
--> key : value
--> 10 : 33

  • 不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引一般以 1 开始。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--实例
local tbl = {"apple", "pear", "orange", "grape"}
for key, val in pairs(tbl) do
    print("Key", key)
end
-- 脚本执行结果为:
--> Key    1
--> Key    2
--> Key    3
--> Key    4

  • table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
a3 = {}
for i = 1, 10 do
    a3[i] = i
end
a3["key"] = "val"
print(a3["key"])
print(a3["none"])
-- 执行结果为:
--> val
--> nil

function (函数)

在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function factorial1(n)
    if n == 0 then
        return 1
    else
        return n * factorial1(n - 1)
    end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))
-- 输出结果为:
--> 120
--> 120

function 可以以匿名函数(anonymous function)的方式通过参数传递:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function testFun(tab,fun)
        for k ,v in pairs(tab) do
                print(fun(k,v));
        end
end


tab={key1="val1",key2="val2"};
testFun(tab,
function(key,val)--匿名函数
        return key.."="..val;
end
);
--输出结果
--> key1 = val1
--> key2 = val2

thread (携程)

在节点精灵中 不生效,已用java thread 做代替

userdata (自定义数据类型)

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。

变量

  • 变量在使用前,需要在代码中进行声明,即创建该变量。
  • 编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。
  • Lua 变量有三种类型:全局变量、局部变量、表中的域。
  • Lua 中的变量全是全局变量,哪怕是语句块或是函数里,除非用 local 显式声明为局部变量。
  • 局部变量的作用域为从声明位置开始到所在语句块结束。
  • 变量的默认值均为 nil。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
a = 5               -- 全局变量
local b = 5         -- 局部变量

function joke()
    c = 5           -- 全局变量
    local d = 6     -- 局部变量
end

joke()
print(c)          --> 5 
print(d)          --> nil

do 
    local a = 6     -- 局部变量
    b = 6           -- 对局部变量重新赋值
    print(a,b);     --> 6 6
end

print(a,b)      --> 5 6

赋值语句

  • 赋值是改变一个变量的值和改变表域的最基本的方法。
1
2
a = "hello" .. "world"
t.n = t.n + 1
  • Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
1
a, b = 10, 2*x       --<>       a=10; b=2*x
  • 遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
1
2
  x, y = y, x                     -- swap 'x' for 'y'
  a[i], a[j] = a[j], a[i]         -- swap 'a[i]' for 'a[j]'
  • 当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- a. 变量个数 > 值的个数             按变量个数补足nil
-- b. 变量个数 < 值的个数             多余的值会被忽略

a, b, c = 0, 1
print(a,b,c)             --> 0   1   nil
 
a, b = a+1, b+1, b+2     -- value of b+2 is ignored
print(a,b)               --> 1   2
 
a, b, c = 0,0,0
print(a,b,c)             --> 0   nil   nil

  • 多值赋值经常用来交换变量,或将函数调用返回给变量:
1
2
3
4
5
a, b = f()
--f()返回两个值,第一个赋给a,第二个赋给b。
-- 应该尽可能的使用局部变量,有两个好处:
--  1.避免命名冲突。
--  2.访问局部变量的速度比全局变量更快。

索引

对 table 的索引使用方括号 []。Lua 也提供了 . 操作。

  • t[i]
  • t.i – 当索引为字符串类型时的一种简化写法
  • gettable_event(t,i) – 采用索引访问本质上是一个类似这样的函数调用
1
2
3
4
5
site = {}
site["key"] = "www.nspirit.cn"
print(site["key"]) --> www.nspirit.cn

print(site.key) --> www.nspirit.cn

循环

很多情况下我们需要做一些有规律性的重复操作,因此在程序中就需要重复执行某些语句。

一组被重复执行的语句称之为循环体,能否继续重复,决定循环的终止条件。

循环结构是在一定条件下反复执行某段程序的流程结构,被反复执行的程序被称为循环体。

循环语句是由循环体及循环的终止条件两部分组成的。

while 循环

Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。

1
2
3
4
5
6
a=10
while( a < 20 )
do
   print("a 的值为:", a)
   a = a+1
end

for 循环

1
2
3
4
5
6
7
8
9
for i=10,1,-1 do
    print(i)
end

--打印数组a的所有值  
a = {"one", "two", "three"}
for i, v in ipairs(a) do
    print(i, v)
end 

流程控制

Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码,在条件为 false 时执行其他指定代码。

  • 控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,true和非nil为真。
  • 要注意的是Lua中 0 为 true:
1
2
3
4
if(0)
then
    print("0 为 true")
end

if 语句

  • Lua if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--[ 定义变量 --]
a = 10;

--[ 使用 if 语句 --]
if( a < 20 )
then
   --[ if 条件为 true 时打印以下信息 --]
   print("a 小于 20" );
end
print("a 的值为:", a);

if else 语句

  • 以下实例用于判断变量 a 的值:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
a = 100;
--[ 检查条件 --]
if( a < 20 )
then
   --[ if 条件为 true 时执行该语句块 --]
   print("a 小于 20" )
else
   --[ if 条件为 false 时执行该语句块 --]
   print("a 大于 20" )
end
print("a 的值为 :", a)
  • Lua if 语句可以与 elseif…else 语句搭配使用, 在 if 条件表达式为 false 时执行 elseif…else 语句代码块,用于检测多个条件语句。

Lua if…elseif…else 语句语法格式如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if( 布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]

elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]

elseif( 布尔表达式 3)
then
   --[ 在布尔表达式 3 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

函数

在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。

Lua 提供了许多的内建函数,你可以很方便的在程序中调用它们,如print()函数可以将传入的参数打印在控制台上。

Lua 函数主要有两种用途:

  • 1.完成指定的任务,这种情况下函数作为调用语句使用;
  • 2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。

函数定义

1
2
3
4
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
    function_body
    return result_params_comma_separated
end
  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
  • function_name: 指定函数名称。
  • argument1, argument2, argument3…, argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
  • function_body: 函数体,函数中需要执行的代码语句块。
  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。

以下实例定义了函数 max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function max(num1, num2)

   if (num1 > num2) then
      result = num1;
   else
      result = num2;
   end

   return result; 
end
-- 调用函数
print("两值比较最大值为 "..max(10,4)) --> 两值比较最大值为     10
print("两值比较最大值为 "..max(5,6)) --> 两值比较最大值为     6
  • Lua 中我们可以将函数作为参数传递给函数,如下实例:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
myprint = function(param)
   print("这是打印函数 -   ##"..param.."##")
end

function add(num1,num2,functionPrint)
   result = num1 + num2
   -- 调用传递的函数参数
   functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)
--输出结果
-- 这是打印函数 -   ##    10    ##
-- 这是打印函数 -   ##    7    ##

  • Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。
1
2
s, e = string.find("www.nspirit.cn", "nspirit") 
print(s..','..e); -- > 5,11

可变参数

Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 … 表示函数有可变的参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function average(...)
   result = 0
   local arg={...}    --> arg 为一个表,局部变量
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. #arg .. " 个数")
   return result/#arg
end

print("平均值为",average(10,5,3,4,5,6))

-- 以上代码执行结果为:
-- 总共传入 6 个数
-- 平均值为    5.5
  • 我们也可以通过 select("#",…) 来获取可变参数的数量:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. select("#",...) .. " 个数")
   return result/select("#",...)
end

print("平均值为"..average(10,5,3,4,5,6))
  • 有时候我们可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:
1
2
3
4
5
6
function fwrite(fmt, ...)  ---> 固定的参数fmt
    return print(string.format(fmt, ...))     
end

fwrite("nspirit\n")       --->fmt = "nspirit", 没有变长参数。  
fwrite("%d%d\n", 1, 2)   --->fmt = "%d%d", 变长参数为 1 和 2
  • 通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(’#’, …) 或者 select(n, …)

select(’#’, …) 返回可变参数的长度。

select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表。

调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回参数列表中从索引 n 开始到 结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数。

1
2
3
4
5
6
7
8
function f(...)
    a = select(3,...)  -->从第三个位置开始,变量 a 对应右边变量列表的第一个参数
    print (a)
end

f(0,1,2,3,4,5) 
-- 输出结果
-- 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
do
function foo(...)
    for i = 1, select('#', ...) do -->获取参数总数
        local arg = select(i, ...); -->读取参数,arg 对应的是右边变量列表的第一个参数
        print("arg:".. arg);
    end
end

foo(1, 2, 3, 4);
end
-- 输出结果
-- 1
-- 2
-- 3
-- 4

运算符

运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 其他运算符

算术运算符

操作符 描述 实例
+ 加法 A + B 输出结果 30
- 减法 A - B 输出结果 -10
* 乘法 A * B 输出结果 200
/ 除法 B / A 输出结果 2
% 取余 B % A 输出结果 0
^ 乘幂 A^2 输出结果 100
- 负号 -A 输出结果 -10
 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
a = 21
b = 10
c = a + b
print("Line 1 - c 的值为 ".. c )
c = a - b
print("Line 2 - c 的值为 ".. c )
c = a * b
print("Line 3 - c 的值为 ".. c )
c = a / b
print("Line 4 - c 的值为 ".. c )
c = a % b
print("Line 5 - c 的值为 ".. c )
c = a^2
print("Line 6 - c 的值为 ".. c )
c = -a
print("Line 7 - c 的值为 ".. c )

--输出结果为
-- Line 1 - c 的值为 31
-- Line 2 - c 的值为 11
-- Line 3 - c 的值为 210
-- Line 4 - c 的值为 2.1
-- Line 5 - c 的值为 1
-- Line 6 - c 的值为 441.0
-- Line 7 - c 的值为 -21

关系运算符

  • 下表列出了 Lua 语言中的常用关系运算符,设定 A 的值为10,B 的值为 20:
操作符 描述 实例
== 等于,检测两个值是否相等,相等返回 true,否则返回 false (A == B) 为 false。
~= 不等于,检测两个值是否相等,不相等返回 true,否则返回 false (A ~= B) 为 true。
> 大于,如果左边的值大于右边的值,返回 true,否则返回 false (A > B) 为 false。
< 小于,如果左边的值大于右边的值,返回 false,否则返回 true (A < B) 为 true。
>= 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false (A >= B) 返回 false。
<= 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false (A <= B) 返回 true。
  • 我们可以通过以下实例来更加透彻的理解关系运算符的应用:
 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
a = 21
b = 10

if( a == b )
then
   print("Line 1 - a 等于 b" )
else
   print("Line 1 - a 不等于 b" )
end

if( a ~= b )
then
   print("Line 2 - a 不等于 b" )
else
   print("Line 2 - a 等于 b" )
end

if ( a < b )
then
   print("Line 3 - a 小于 b" )
else
   print("Line 3 - a 大于等于 b" )
end

if ( a > b ) 
then
   print("Line 4 - a 大于 b" )
else
   print("Line 5 - a 小于等于 b" )
end

-- 修改 a 和 b 的值
a = 5
b = 20
if ( a <= b ) 
then
   print("Line 5 - a 小于等于  b" )
end

if ( b >= a ) 
then
   print("Line 6 - b 大于等于 a" )
end
-- 脚本输出:
-- Line 1 - a 不等于 b
-- Line 2 - a 不等于 b
-- Line 3 - a 大于等于 b
-- Line 4 - a 大于 b
-- Line 5 - a 小于等于  b
-- Line 6 - b 大于等于 a

逻辑运算符

下表列出了 Lua 语言中的常用逻辑运算符,设定 A 的值为 true,B 的值为 false:

操作符 描述 实例
and 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 (A and B) 为 false。
or 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 (A or B) 为 true。
not 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 not(A and B) 为 true。

我们可以通过以下实例来更加透彻的理解逻辑运算符的应用:

 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
a = true
b = true

if ( a and b )
then
   print("a and b - 条件为 true" )
end

if ( a or b )
then
   print("a or b - 条件为 true" )
end

print("---------分割线---------" )

-- 修改 a 和 b 的值
a = false
b = true

if ( a and b )
then
   print("a and b - 条件为 true" )
else
   print("a and b - 条件为 false" )
end

if ( not( a and b) )
then
   print("not( a and b) - 条件为 true" )
else
   print("not( a and b) - 条件为 false" )
end

--输出
-- a and b - 条件为 true
-- a or b - 条件为 true
---------分割线---------
-- a and b - 条件为 false
-- not( a and b) - 条件为 true

其他运算符

操作符 描述 实例
.. 连接两个字符串 a..b ,其中 a 为 “Hello " , b 为 “World”, 输出结果为 “Hello World”。
# 一元运算符,返回字符串或表的长度。 #“Hello” 返回 5

我们可以通过以下实例来更加透彻的理解连接运算符与计算表或字符串长度的运算符的应用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
a = "Hello "
b = "World"

print("连接字符串 a 和 b ", a..b )

print("b 字符串长度 ",#b )

print("字符串 Test 长度 ",#"Test" )

print("节点精灵网址长度 ",#"www.nspirit.cn" )

-- 以上程序执行结果为:
-- 连接字符串 a 和 b     Hello World
-- b 字符串长度     5
-- 字符串 Test 长度     4
-- 菜鸟教程网址长度     14

运算符优先级

从高到低的顺序:

1
2
3
4
5
6
7
8
^
not    - (unary)
*      /       %
+      -
..
<      >      <=     >=     ~=     ==
and
or

除了 ^ 和 .. 外所有的二元运算符都是左连接的。

1
2
3
4
5
a+i < b/2+1          <-->       (a+i) < ((b/2)+1)
5+x^2*8              <-->       5+((x^2)*8)
a < y and y <= z     <-->       (a < y) and (y <= z)
-x^2                 <-->       -(x^2)
x^y^z                <-->       x^(y^z)
  • 我们可以通过以下实例来更加透彻的了解 Lua 语言运算符的优先级:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
a = 20
b = 10
c = 15
d = 5

e = (a + b) * c / d;-- ( 30 * 15 ) / 5
print("(a + b) * c / d 运算值为  :"..e )

e = ((a + b) * c) / d; -- (30 * 15 ) / 5
print("((a + b) * c) / d 运算值为 :"..e )

e = (a + b) * (c / d);-- (30) * (15/5)
print("(a + b) * (c / d) 运算值为 :"..e )

e = a + (b * c) / d;  -- 20 + (150/5)
print("a + (b * c) / d 运算值为   :"..e )
--输出结果
-- (a + b) * c / d 运算值为  :     90.0
-- ((a + b) * c) / d 运算值为 :    90.0
-- (a + b) * (c / d) 运算值为 :    90.0
-- a + (b * c) / d 运算值为   :    50.0

字符串

字符串或串(String)是由数字、字母、下划线组成的一串字符。

Lua 语言中字符串可以使用以下三种方式来表示:

  • 单引号间的一串字符。
  • 双引号间的一串字符。
  • [[ 与 ]] 间的一串字符。

以上三种方式的字符串实例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
string1 = "Lua"
print("\"字符串 1 是\""..string1)
string2 = 'nspirit.com'
print("字符串 2 是"..string2)

string3 = [["Lua 教程"]]
print("字符串 3 是"..string3)

-- "字符串 1 是"    Lua
-- 字符串 2 是    nspirit.cn
-- 字符串 3 是    "Lua 教程"

转义字符

转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。如在字符串转换双引号可以使用 “""。

所有的转义字符和所对应的意义:

转义字符 意义 ASCII码值(十进制)
\a 响铃(BEL) 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\ 代表一个反斜线字符’'' 092
' 代表一个单引号(撇号)字符 039
" 代表一个双引号字符 034
\0 空字符(NULL) 000
\ddd 1到3位八进制数所代表的任意字符 三位八进制
\xhh 1到2位十六进制所代表的任意字符 二位十六进制

字符串操作

Lua 提供了很多的方法来支持字符串的操作:

  • 字符串全部转为大写字母。
1
2
res = string.upper("abcde"):
print(res) --> ABCDE
  • 字符串全部转为小写字母。
1
2
res = string.lower("AbCe"):
print(res) --> abce
  • 在字符串中替换。 string.gsub(mainString,findString,replaceString,num)

mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:

1
2
string.gsub("aaaa","a","z",3);
-- zzza 3
  • 在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。

以下实例查找字符串 “Lua” 的起始索引和结束索引位置:

1
string.find("Hello Lua user", "Lua", 1)  --> 7  9
  • 字符串反转 string.reverse(arg)
1
2
res = string.reverse("Lua")
print(res) --> auL
  • 返回一个类似printf的格式化字符串 string.format(…)
1
2
res =  string.format("the value is:%d",4)
print(res) -->the value is:4
  • char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。
1
2
3
4
5
6
res = string.char(97,98,99,100)
print(res) --> abcd
res =  string.byte("ABCD",4)
print(res) --> 68
res = string.byte("ABCD")
print(res) --> 65
  • 计算字符串长度。 string.len(arg)
1
2
res = string.len("abc")
print(res)
  • 返回字符串string的n个拷贝 string.rep(string, n)
1
2
string.rep("abcd",2)
--> abcdabcd
  • 链接两个字符串 ..
1
print("www.nspirit.".."cn") --> www.nspirit.cn
  • 回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
1
2
3
4
5
for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
-- 输出结果
-- Hello
-- Lua
-- user
  • string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。

在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

1
2
3
4
5
string.match("I have 2 questions for you.", "%d+ %a+")
--> 2 questions

string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
--> 2, "questions"

字符串截取

字符串截取使用 sub() 方法。

string.sub() 用于截取字符串,原型为:

string.sub(s, i [, j])

参数说明:

  • s:要截取的字符串。
  • i:截取开始位置。
  • j:截取结束位置,默认为 -1,最后一个字符。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
-- 字符串
local sourcestr = "prefix--nspiritgoogletaobao--suffix"
print("\n原始字符串".. string.format("%q", sourcestr))

-- 截取部分,第4个到第15个
local first_sub = string.sub(sourcestr, 4, 15)
print("\n第一次截取".. string.format("%q", first_sub))

-- 取字符串前缀,第1个到第8个
local second_sub = string.sub(sourcestr, 1, 8)
print("\n第二次截取".. string.format("%q", second_sub))

-- 截取最后10个
local third_sub = string.sub(sourcestr, -10)
print("\n第三次截取".. string.format("%q", third_sub))

-- 索引越界,输出原始字符串
local fourth_sub = string.sub(sourcestr, -100)
print("\n第四次截取".. string.format("%q", fourth_sub))

字符串格式化

Lua 提供了 string.format() 函数来生成具有特定格式的字符串, 函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。

由于格式字符串的存在, 使得产生的长字符串可读性大大提高了。这个函数的格式很像 C 语言中的 printf()。

以下实例演示了如何对字符串进行格式化操作:

格式字符串可能包含以下的转义码:

  • %c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
  • %d, %i - 接受一个数字并将其转化为有符号的整数格式
  • %o - 接受一个数字并将其转化为八进制数格式
  • %u - 接受一个数字并将其转化为无符号整数格式
  • %x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
  • %X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
  • %e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
  • %E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
  • %f - 接受一个数字并将其转化为浮点数格式
  • %g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
  • %q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
  • %s - 接受一个字符串并按照给定的参数格式化该字符串 为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:
  • (1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
  • (2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
  • (3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
  • (4) 宽度数值
  • (5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
string1 = "Lua"
string2 = "Tutorial"
number1 = 10
number2 = 20
-- 基本字符串格式化
print(string.format("基本格式化 %s %s",string1,string2))
-- 日期格式化
date = 2; month = 1; year = 2014
print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
-- 十进制格式化
print(string.format("%.4f",1/3))

其他例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
string.format("%c", 83)                 -- 输出S
string.format("%+d", 17.0)              -- 输出+17
string.format("%05d", 17)               -- 输出00017
string.format("%o", 17)                 -- 输出21
string.format("%u", 3.14)               -- 输出3
string.format("%x", 13)                 -- 输出d
string.format("%X", 13)                 -- 输出D
string.format("%e", 1000)               -- 输出1.000000e+03
string.format("%E", 1000)               -- 输出1.000000E+03
string.format("%6.3f", 13)              -- 输出13.000
string.format("%q", "One\nTwo")         -- 输出"One\
                                        --   Two"
string.format("%s", "monkey")           -- 输出monkey
string.format("%10s", "monkey")         -- 输出    monkey
string.format("%5.3s", "monkey")        -- 输出  mon

字符与整数相互转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 字符转换
-- 转换第一个字符
print(string.byte("Lua"))
-- 转换第三个字符
print(string.byte("Lua",3))
-- 转换末尾第一个字符
print(string.byte("Lua",-1))
-- 第二个字符
print(string.byte("Lua",2))
-- 转换末尾第二个字符
print(string.byte("Lua",-2))

-- 整数 ASCII 码转换为字符
print(string.char(97))

--[[
输出如下:
76
97
97
117
117
a
--]]

匹配模式

Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。

你还可以在模式串中使用字符类。

字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类 %d 匹配任意数字。所以你可以使用模式串 %d%d/%d%d/%d%d%d%d 搜索 dd/mm/yyyy 格式的日期:

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

下面的表列出了Lua支持的所有字符类:

单个字符(除 ^$()%.[]*+-? 外): 与该字符自身配对

  • .(点): 与任何字符配对

  • %a: 与任何字母配对

  • %c: 与任何控制符配对(例如\n)

  • %d: 与任何数字配对

  • %l: 与任何小写字母配对

  • %p: 与任何标点(punctuation)配对

  • %s: 与空白字符配对

  • %u: 与任何大写字母配对

  • %w: 与任何字母/数字配对

  • %x: 与任何十六进制数配对

  • %z: 与任何代表0的字符配对

  • %x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对

  • [数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对

当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,’%A’非字母的字符:

1
2
local a,b= string.gsub("hello, up-down!", "%A", ".")
print(a..b) -->hello..up.down.4

数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。

在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:

1
( ) . % + - * ? [ ^ $

‘%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;’%%’ 匹配字符 ‘%’。转义字符 ‘%‘不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。

  • 单个字符类匹配该类别中任意单个字符;
  • 单个字符类跟一个 ‘*’, 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 ‘+’, 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 ‘-’, 将匹配零或更多个该类的字符。 和 ‘*’ 不同, 这个条目总是匹配尽可能短的串;
  • 单个字符类跟一个 ‘?’, 将匹配零或一个该类的字符。 只要有可能,它会匹配一个;
  • %n, 这里的 n 可以从 1 到 9; 这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。
  • %bxy, 这里的 x 和 y 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 x 和 y 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x 就 +1 ,读到一个 y 就 -1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
  • %f[set], 指 边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 ‘\0’ 一样。

模式:

模式 指一个模式条目的序列。 在模式最前面加上符号 ‘^’ 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 ‘$’ 将使匹配过程锚定到字符串的结尾。 如果 ‘^’ 和 ‘$’ 出现在其它位置,它们均没有特殊含义,只表示自身。

捕获:

模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 “(a*(.)%w(%s*))” , 字符串中匹配到 “a*(.)%w(%s*)” 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 “.” 匹配到的字符是 2 号捕获物, 匹配到 “%s*” 的那部分是 3 号。 作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 “()aa()” 作用到字符串 “flaaap” 上,将产生两个捕获物: 3 和 5 。

表 table

table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。

Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。

Lua table 是不固定大小的,你可以根据自己需要进行扩容。

Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。

table(表)的构造

构造器是创建和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表。可以直接初始化数组:

1
2
3
4
5
6
7
8
9
-- 初始化表
mytable = {}

-- 指定值
mytable[1]= "Lua"

-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存

当我们为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,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
-- 简单的 table
mytable = {}
print("mytable 的类型是 "..type(mytable))

mytable[1]= "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1 的元素是 ".. mytable[1])
print("mytable 索引为 wow 的元素是 ", mytable["wow"])

-- alternatetable和mytable的是指同一个 table
alternatetable = mytable

print("alternatetable 索引为 1 的元素是 ".. alternatetable[1])
print("mytable 索引为 wow 的元素是 ".. alternatetable["wow"])

alternatetable["wow"] = "修改后"

print("mytable 索引为 wow 的元素是 ".. mytable["wow"])

-- 释放变量
alternatetable = nil
print("alternatetable 是 ".. alternatetable)

-- mytable 仍然可以访问
print("mytable 索引为 wow 的元素是 ".. mytable["wow"])

mytable = nil
print("mytable 是 ".. mytable)
-- 输出结果是
--[[
mytable 的类型是     table
mytable 索引为 1 的元素是     Lua
mytable 索引为 wow 的元素是     修改前
alternatetable 索引为 1 的元素是     Lua
mytable 索引为 wow 的元素是     修改前
mytable 索引为 wow 的元素是     修改后
alternatetable 是     nil
mytable 索引为 wow 的元素是     修改后
mytable 是     nil
]]--

table 操作

以下列出了 Table 操作常用的方法:

    1. table.concat (table [, sep [, start [, end]]]):

concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。

    1. table.insert (table, [pos,] value):

在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.

    1. table.maxn (table)

指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现)

    1. table.remove (table [, pos])

返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。

    1. table.sort (table [, comp])

对给定的table进行升序排序。

table 连接

我们可以使用 concat() 输出一个列表中元素连接成的字符串:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 "..table.concat(fruits))

-- 指定连接字符
print("连接后的字符串 "..table.concat(fruits,", "))

-- 指定索引来连接 table
print("连接后的字符串 "..table.concat(fruits,", ", 2,3))
--[[
输出结果
连接后的字符串     bananaorangeapple
连接后的字符串     banana, orange, apple
连接后的字符串     orange, apple
]]--

插入和移除

以下实例演示了 table 的插入和移除操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
fruits = {"banana","orange","apple"}

-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 "..fruits[4])

-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 "..fruits[2])

print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 "..fruits[5])
-- 输出结果
--[[
索引为 4 的元素为     mango
索引为 2 的元素为     grapes
最后一个元素为     mango
移除后最后一个元素为     nil
]]--

table 排序

以下实例演示了 sort() 方法的使用,用于对 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
fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
        print(k..v)
end

table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
        print(k..v)
end
--输出结果
--[[
   排序前
1    banana
2    orange
3    apple
4    grapes
排序后
1    apple
2    banana
3    grapes
4    orange
]]--

迭代器

迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。

在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。

泛型 for 迭代器

  • 泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
  • 泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:
1
2
3
4
--遍历所有内容,包括键值
for k, v in pairs(t) do
    print(k, v)
end

上面代码中,k, v为变量列表;pairs(t)为表达式列表。

查看以下实例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
--遍历下标内容,不包括键值
array = {"Google", "Runoob"}

for key,value in ipairs(array) 
do
   print(key.. value)
end

--输出结果
--[[
1  Google
2  Runoob
]]--

模块

、 在节点精灵 中 require 函数可以加载其他文件中的lua代码

加载路径

在节点精灵里,所有文件加载必须填写绝对路径。

根目录 为 ‘/’

  • 如图

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/filejg.jpg

文件层级加载相关

  • 相同层级的文件加载如下
1
2
3
4
-- mian.lua
-- a.lua
--在mian.lua 中 如下就可以加载a.lua文件了
   require('a')
  • 不同层级的文件加载
1
2
3
4
5
-- mian.lua
-- a(这是文件夹)
   -- b.lua(这是a文件夹中的文件)
--在mian.lua 中 如下就可以加载b.lua文件了
   require('a/b')

多重加载

在节点精灵中, 可以在lua文件中 require 其他文件

1
2
3
4
5
6
--文件结构如下:
-- mian.lua
-- a.lua
-- b.lua

--在mian.lua 中 加载a.lua 在a.lua 中可以加载 b.lua
  • mian.lua 中如下
1
   require('a')
  • a.lua 中如夏
1
   require('b')