第6章 Lua
标准库
在我们前面就已经学习了很多Lua标准库里的函数,我们不会涵盖所有的函数,我们只介绍在开发插件时使用最普通的函数。如果你想了解细节,请访问http://www.lua.org/manual/5.1。这是一个完整的Lua参考文档,你可以把它们打印出来。
另外,你可以参考Lua的首席架构师编写的一本书《Programing in Lua》(默然说话:在网上可以很容易找到这本书的电子版本)。
本章里所介绍的每一个函数都首先呈现给大家它们的原型。一个完整的函数原型描述了这个函数的返回值,以及这个函数的输入参数。例如:
var=foo(arg1,arg2)
这里,函数名叫foo,它需要两个必填的参数arg1和arg2,然后有一个返回值。如果出现的参数是可选的,我们会使用方括号把它们括起来:
var=foo(arg1[,arg2])
这个记号表示foo的第二个参数是一个可选参数,也就意味着foo函数有两个版本的使用方式,传一个参数的版本和传二个参数的版本。
我们所列出的函数原型与前面所描述的有一点不同,我们都不列出它们的返回值的个数,这一点将在函数说明中列出
这个函数将一个表的所有数组部分连接在一起,函数返回一个字符串。你可以指定在连接表中数组部分各元素的分隔符sep,如果你不指定,则默认不使用任何符号来分隔表元素。我们还可以指定连接时数组的开始下标i和结束下标j,如果j大于i,则返回一个空字符串:
> tbl={"苹果","香蕉","梨"}
> print(table.concat(tbl))
苹果香蕉梨
> print(table.concat(tbl,","))
苹果,香蕉,梨
> print(table.concat(tbl,",",1,2))
苹果,香蕉
> print(table.concat(tbl,"/n",2,3))
香蕉
梨
我们可以看到,分隔符可以是任何字符(包括换行符)。这个函数是显示表数组部分元素的一个简便易用的一个工具。
这个函数没有返回值,它将一个新的元素插入到数组当中,如果不指定可选的位置参数(pos),则将新元素添加到数组的未尾。
> tbl={"苹果","香蕉","梨"}
> table.insert(tbl,"桔子")
> print(table.concat(tbl,","))
苹果,香蕉,梨,桔子
> table.insert(tbl,3,"黄果")
> print(table.concat(tbl,","))
苹果,香蕉,黄果,梨,桔子
这个函数返回一个数字,它代表给定表的最大正索引,和“#”求得的数组个数不一样,它所获得的最大正索引并不要求是连续的,另外,这个数字可以是整数,也可以是小数。如果给定表中找不到正的数字索引,则返回零:
> tbl={"a","b","c",[26]="d"}
> print(#tbl)
3
> print(table.maxn(tbl))
26
> tbl[29.32]="e"
> print(table.maxn(tbl))
29.32
这个函数将一个元素从给定的表中移除,它的返回值就是那个被移除的元素,如果没有给定移除元素的位置(pos),则移除数组中的最后一个元素。
> tbl={"苹果","香蕉","梨"}
> print(table.remove(tbl))
梨
> print(table.concat(tbl,","))
苹果,香蕉
这个函数将给定表的数组部分排序,如果给定了comp,那么comp必须是一个函数,它必须有两个参数,这两个参数就是给定表中的两个元素,在comp函数中必须完成比较两个参数,并返回它们比较的布尔值。
table.sort()不是很稳定,一旦比较的两个元素是相等的,那么就说不准谁在前谁在后了。
函数
|
描述
|
例子
|
math.abs(x)
|
返回
x
的绝对值
|
> print(math.abs(13))
13
> print(math.abs(-13))
13
|
math.ceil(x)
|
返回大于或等于
x
的最小整数
|
> print(math.ceil(1.03))
2
> print(math.ceil(-1.03))
-1
|
math.deg(x)
|
返回所给弧度
x
所对应的角度
|
> print(math.deg(math.pi))
180
> print(math.deg(math.pi*2.5))
450
|
math.exp(x)
|
返回
ex
的值
|
> print(math.exp(27))
532048240601.8
|
math.floor(x)
|
返回小于或等于
x
的最小整数
|
> print(math.floor(1.99))
1
> print(math.floor(-1.99))
-2
|
math.fmod(x,y)
|
返回
x
除以
y
得到的余数,你可以用
x%y
得到相同的结果
|
> print(math.fmod(14,3))
2
> print(math.fmod(14,2))
0
|
math.log(x)
|
返回
x
的自然对数
|
> print(math.log(532048240601.8))
27
|
math.log10(x)
|
返回以
10
为底
x
的对数
|
> print(math.log10(100))
2
|
math.max(x,y,z,…)
|
返回参数表中的最大值
|
> print(math.max(100,7,-12))
100
|
math.min(x,y,z,…)
|
返回参数表中的最小值
|
> print(math.min(100,7,-12))
-12
|
math.modf(x)
|
返回两个参数,一个数的整数部分和小数部分
|
> print(math.modf(10.23))
10 0.23
> print(math.modf(7/3))
2 0.33333333333333
|
math.pi
|
数学常数π的值
|
> print(math.pi)
3.1415926535898
|
math.pow(x,y)
|
返回
x
的
y
次方
|
> print(math.pow(2,10))
1024
|
math.rad(x)
|
返回给定的
x
角度所对应的弧度
|
> print(math.rad(180))
3.1415926535898
|
math.random([m[,n]])
|
生成伪随机数,在没有参数时产生
0-1
之间的伪随机小数,如果有
m
则产生
1~m
(包含
m
)的伪随机整数,如果有
n
则生成
m~n
(包含
m
和
n
)的伪随机整数
|
> print(math.random())
0.0012512588885159
> print(math.random(100))
57
> print(math.random(10,20))
12
|
math.randomseed(x)
|
为
Lua
生成一个种子来产生一系列的伪随机数,一个相同的种子产生出来的序列总是相同的
|
> math.randomseed(1000)
> print(math.random(100))
11
> print(math.random(100))
26
--
重置种子
> math.randomseed(1000)
> print(math.random(100))
11
> print(math.random(100))
26
|
math.sqrt(x)
|
返回
x
的平方根
|
> print(math.sqrt(169))
13
> print(math.sqrt(2))
1.4142135623731
|
Lua提供的字符串函数既可以面向对象的形式调用,同时也可以使用库函数调用的形式。
> test="这是一个字符串"
> print(string.len(test))
14
> print(test:len())
14
函数
|
描述
|
例子
|
string.len(s)
|
接收一个字符串并返回其长度,空字符串
””
的长度是
0
。
|
> print(string.len("Monkey"))
6
|
string.lower(s)
|
接收一个字符串并返回一份拷贝,当中所有大写字母被转换成小写。
|
> test="Hello World"
> print(string.lower(test))
hello world
|
string.rep(s,n)
|
返回一个字符串,由连续
n
个的字符串
s
组成
|
> print(string.rep("Hello",3))
HelloHelloHello
|
string.reverse(s)
|
将
s
逆序之后返回
|
> print(string.reverse("Hello"))
olleH
|
string.sub(s,i[,j])
|
返回
s
的一个子串,下标从
i
开始直到
j
(
Lua
中字符串的下标位置起始值为
1
),如果没有
j
,则截取到字符串的尾部。
i
和
j
都可以是负数,如果是负数,则表示从尾部向头部计算坐标(最后一个字符的下标是
-1
,以此类推)
|
> print(string.sub("Hello",3,5))
llo
> print(string.sub("Hello",2))
ello
> print(string.sub("Hello",-5,-3))
Hel
|
string.upper(s)
|
接收一个字符串并返回一个副本,当中所有小写字母都被转换成大写字母。
|
> print(string.upper("Hello World"))
HELLO WORLD
|
string.format(格式化字符串,…)函数会根据所定义的格式化字符串,格式化参数表中的参数。
string.format()函数用来格式化任意多个参数,并且基于格式化字符串进行输出。一个格式化字符串可以包含普通的字母字符和特殊的转换代码:
l %c——取一个数字参数然后用对应ASCII码的字符代替这个位置。
l %d,%i——取一个数字参数,将其格式化为一个带符号整数。
l %o——取一个数字参数,将其格式化为八进制数。
l %u——取一个数字参数并将其格式化为无符号整数
l %x——取一个数字参数并将其格式化为十进制数,使用小写字母。
l %X——取一个数字参数并将其格式化为十六进制数,使用大写字母。
l %e——取一个数字参数并使用科学计数法进行格式化,其中的e使用小写字母。
l %E——取一个数字参数并使用科学计数法进行格式化,其中的e使用大写字母。
l %f——取一个数字参数并将其格式化为浮点数。
l %g,%G——取一个数字,按照较短原则转换成%e或%f,如果你使用%G,则会转换为%E或%F。
l %q——格式化一个字符串,使得它可以安全地被Lua解释器读取。
l %s——接收一个字符串,并且按照给定的选项进行格式化。
在百分号和指示符之间可以有几个选项,下面按它们出现的顺序排列:
l 一个正号的指定(+)会使一个数字输出时总带有这个符号,而默认情况下,正数不显示正号,而负数才会显示负号。
l 一个填充字符(空格或者是0)可以在一个指定宽度的输出中填充多余的空位,在默认情况下,这个填充是空格——只在指定了输出宽度的情况下才有填充,无论是默认的情况,还是你指定填充字符的情况都是如此。
l 一个减号(-)表示输出进行左对齐,默认情况下是右对齐。
l %5.3d是指返回的字符串是整数,最少应该占有3个字符的宽度,如果少于这个宽度则在左边用0填充。
l %5.3f是指返回的字符串是小数,保留3位小数。如果少于这个宽度则在右边用0填充。
命令
|
结果
|
print(string.format(“%c",83))
|
S
(
默然说话:%c表示以字符的形式显示后面那个数字,而大写S的ASCII编码就是83)
|
print(string.format("%+d",17.0))
|
+17
(
默然说话:%d表示以整数的方式来显示后面那个数字,+号表示在后面的那个数字为正数时显示正号
)
|
print(string.format("%05d",17.0))
|
00017
(
默然说话:%d表示以整数的方式来显示后面那个数字,5表示后面那个数字的显示要占用5个字符的位置,0表示如果有多余的位置用0进行填充。
)
|
print(string.format("%o",17.0))
|
21
(
默然说话:%o表示以八进制的方式来显示后面那个数字
)
|
print(string.format("%u",17.5))
|
17
(
默然说话:%u表示以无符号整数的方式来显示后面那个数字
)
|
print(string.format("%x",15.5))
|
f
|
print(string.format("%X",15.5))
|
F
|
在编写插件的时候您可能会发现一个常见的情况:需要用给定的模式来匹配和分析游戏的文本。Lua提供了一些辅助函数来完成这些任务。这些函数可以用模式来描述根据一个给定的字符串如何在文本中搜索。
(
默然说话:这里使用了一个很让人蛋疼的名词:正则表达式,主要的目的有两个:第一:表示我的用词很专业(坏笑),第二:破除正则表达式神秘感,并且告诉大家正则表达式并不神秘!正则表达式其实就是一种模式匹配的算法,设计正则表达式引擎不容易,但使用它却相对简单。当然,大家如果想进一步了解关于正则表达式的方方面面,默然正在筹划这方面的一个系列文章,到时还希望大家继续捧场。)
正则表达式就是使用一些被称为元字符的符号来代表某种基本符号,比如使用%s代表所有的空白字符,使用%w代表所有的数字或字母等等,之所以叫“元字符”有两个原因,第一:它们是一个正则表达式的最最基本的符号,第二:给它们另取一个名字,好和普通的字符有所区别。元字符都是已经规定好的,需要大家死记硬背的符号。简单的正则表达式使用较少的元字符,比如Windows操作系统就使了两个符号,一个是星号(*)另一个是问号(?),它们是在搜索某一类文件时我们会使用的符号(如*.txt代表了所有任意字符的文本文件之类),而复杂一些的正则表达式则使用了较多的元字符,但它们的功能也就非常强大。Lua的正则表达式就属于后者。
(
默然说话:其实现在几乎所有流行的编程语言都附带正则表达式引擎,只是在使用它们的时候用法不太一样,元字符的表达方式也有些区别,但是本质上它们在使用上几乎是没有区别的,你学会了其中一种用法,那也就几乎可以在任意语言中使用正则表达式了。)
要学习正则表达式,第一步就要先了解一下它们都有哪些元字符,各自代表什么意思。
表6-4
元字符
元字符符号
|
描述
|
x
|
字符
x
本身
|
.(
句点)
|
任意字符
|
%a
|
任意字母
|
%c
|
任意控制字符
|
%d
|
任意数字
|
%l
|
任意小写字母
|
%p
|
任意括号字符
|
%s
|
任意空白字符
|
%u
|
任意大写字母
|
%w
|
任意数字或字母
|
%x
|
任意十六进制数字
|
%z
|
表示
0
的字符
(
在计算机里表示
0
个字符不止一个,有很多的,这里就不多介绍了以免引起混乱
)
|
[
字符集合]
|
任意在集合中的字符,可以由一个减号(
-
)来表示某个顺序的符号
|
[^
字符集合]
|
任意不在集合中的字符。
|
另外,在前所提到的各种字符的集合,如%p、%s等都可以写成大写字母的形式,但是它们所代表的意思是小写字母的补集,如%P就是任何的非括号字符,%S就是任意的非空白字符等等。
前面定义的所有元字符,还可以加上一些选项,用来表示这个元字符是用来匹配一个字符位置还是用来匹配多个字符位置,以及是匹配更可能多的字符,还是匹配更可能少的字符。
类别
|
描述
|
单个元字符
|
任意该元字符所匹配的一个字符位置
|
单个元字符,后接*
|
0
个或多个字符位置。匹配尽可能长的序列
|
单个元字符,后接+
|
1
个或多个字符位置。匹配尽可能长的序列
|
单个元字符,后接-
|
0
个或多个字符位置,匹配尽可能短的序列
|
单个元字符,后接?
|
0
个或
1
个字符位置。
|
%n
|
n
可以是
1-9
之间的数字,表示第
n
个可匹配的子串
|
%bxy
,x
和y
为不同的字符
|
以
x
开头,
y
结尾的字符串。
|
(
默然说话:上面的几张表一定看得大家都晕了吧?没关系,我也一样晕,这几张表其实就象字典一样,这里先大概看看,了解一下内容,后面在具体应用时大家再对照这几张表就可以了。)
捕获的意思就是正则表达式找到了匹配小括号内的项。当捕获成功之后,这些被捕获的项就可以被保存下来备用。
在模式的开头使用^表示匹配的开头,在模式的结尾使用$来表示匹配的结束。
类别
|
描述
|
1
到多个非空字符,
|
%S+
|
以MYADDON:
开头的字符串
|
^MYADDON:(.+)
|
匹配1
到多位的数字,这个数字可以有小数点,也可以没有
|
(%d+%.?%d*)
|
匹配一个赋值语句,
|
(
%w+
)
=(%S+)
|
单个元字符,后接?
|
1
个字符位置。
|
%n
|
n
可以是
1-9
之间的数字,表示第
n
个可匹配的子串
|
%bxy
,x
和y
为不同的字符
|
以
x
开头,
y
结尾的字符串。
|
Lua提供了4个函数来接收模式字符串:
l string.gmatch(s,pattern)
l string.gsub(s,pattern,rep[,n])
l string.match(s,pattern[,init])
l string.find(s,pattern[,init[,plain]])
这些函数同样对字符串本身的面向对象的方法调用有效,这一点和前面所讨论的辅助函数一样。
string.gmatch()函数返回一个迭代器函数,每次调用它都返回字符串s中模式pattern的下一个捕获。如果模式没有捕获,那么每次调用都会产生一个完全匹配。
下面的循环依次捕获字符串s中的一个单词,每行输出一个,由于这个正则表达式没有捕获(没有写在小括号里的项),所以它返回了全匹配:
> s="hello world from Lua"
> for w in s:gmatch("%a+") do
>> print(w)
>> end
hello
world
from
Lua
下面是将一个格式字符串转化为表中的数据的例子,这里的正则表达式有两个捕获(等号左边一个,等号右边一个),所以在for循环设置了两个变量来接收这两个捕获:
> t={}
> s="from=world,to=Lua"
> for k,v in s:gmatch("(%w+)=(%w+)") do
>> t[k]=v
>> end
> for k,v in pairs(t) do
>> print(k,v);
>> end
to Lua
from world
string.gsub(s,pattern,rep[,n])函数有三个参数,s表示目标串,pattern表示匹配模式,rep表示替换串。它的作用就是在s中取出与pattern匹配的子串,并使用rep去替换它。函数会返回两个值,一个是经过替换之后的字符串,另一个是被替换的匹配的数目。下面就是一个将s中的每个单词都替换为”ttt”的写法(最后输出的2表示进行了两次替换操作)。
> print(string.gsub("Hello World","(%w+)","ttt"))
ttt ttt 2
其中第二个参数是(%w+),其中%w表示一个数字或字母,%w+表示一个或多个连续数字或字母组成的字符串,加了小括号表示它是一个捕获。根据这个模式我们能看出,从”Hello World”这个字符串中能产生两个捕获:一个是”Hello”另一个是”World”。它们都被第三个参数”ttt”替换了,所以我们看到最后的输出是”ttt ttt”,后面的2表示有两个地方被替换了。
(
默然说话:费了九牛二虎之力,参考了其他的几本书,总算写到这里。。。。由于原书关于string.match()函数的介绍实在是很神秘,无论是原版英文还是翻译版我都没有弄明白,所以这里我就暂时不作说明了,等到后面具体运用到的时候再具体说明吧。纠结呀。。。。。)
在WoW的Lua中,添加了几个新的函数,以便为开发人员提供帮助:
l strsplit(sep,str)
l strjoin(sep,…)
l strconcat(…)
l getglobal(name)
l setglobal(name,value)
l debugstack([start[,count1[,count2]]])
这些函数在WoWLua插件中是可用的,在WebLua的网页中和本书网站所提供的解释器中也是可用的,但是从其他地方所获取的Lua版本中就可能不能用了。
strsplit(sep,str)函数获取一个给定的字符串str,然后在每次出现分拆字符串sep的地方将它分拆成一些子字符串。这个函数会向调用者返回每个独立的字符串(不包括分拆字符串)。
> print(strsplit(":","foo:bar:blah"))
foo bar blah、
strjoin(sep,…)获取一列字符串然后将它们用分隔字符串sep作为间隔连接起来,并返回结果。
> print(strjoin(" ","this","is"))
this is
strconcat(…)函数获取一列字符串并且将它们拼接成一个长字符串返回。
> print(strconcat("this","is","a","test"))
thisisatest
getglobal(name)函数以一个字符串作为输入,获取这个字符串所代表的变量名。如果存在,则返回这个名称的全局变量,如果不存在,则返回nil。
> greek1,greek2,greek3="香蕉","苹果","梨"
> g=getglobal("greek1")
> print(g)
香蕉
setglobal(name,value)函数获取一个变量名字符串和一个对应的值,然后将该名称的变量设置为这个值。
> print(m)
nil
> setglobal("m",17)
> print(m)
17
WoW中,许多库函数被赋予一个更短的别名,以使它们更容易地被使用和输入。表6-10列出了这些别名。
别名
|
原函数
|
别名
|
原函数
|
abs
|
math.abs
|
tan
|
math.tan
|
acos
|
math.acos
|
format
|
string.format
|
asin
|
math.asin
|
gmatch
|
string.gmatch
|
atan
|
math.atan
|
gsub
|
string.gsub
|
atan2
|
math.atan2
|
strbyte
|
string.byte
|
ceil
|
math.ceil
|
strchar
|
string.char
|
cos
|
math.cos
|
strfind
|
string.find
|
deg
|
math.deg
|
strlen
|
string.len
|
exp
|
math.exp
|
strlower
|
string.lower
|
floor
|
math.floor
|
strmatch
|
string.match
|
frexp
|
math.frexp
|
strrep
|
string.rep
|
ldexp
|
math.ldexp
|
strrev
|
string.reverse
|
log
|
math.log
|
strsub
|
string.sub
|
max
|
math.max
|
strupper
|
string.upper
|
min
|
math.min
|
foreach
|
table.foreach
|
mod
|
math.fmod
|
foreachi
|
table.foreachi
|
rad
|
math.rad
|
getn
|
table.getn
|
random
|
math.random
|
sort
|
table.sort
|
Randomseed
|
math.randomseef
|
tinsert
|
table.insert
|
sin
|
math.sin
|
tremove
|
table.remove
|
sqrt
|
math.sqrt
|
|
|
Lua有3个主要的辅助函数库。表库提供了对数组表进行插入、移除和排序的方法。字符串有一些有用的方法来完成诸如将字符串转成小写、大写或者反置的工作。除此之外,本章还介绍了Lua的正则表达式。