一.Lua理解
Lua可以被理解成为不用编译就能运行的语言,所以被广泛用于热更新.
二.环境安装
1.SciTE:包括Lua控制台和IDE集成工具.控制台无法保存,工具类似于记事本可以保存脚本,还能编译运行(虽然Lua不用编译,但可以选择只编译不运行,编译后其实是生成一个中间文件).
下载地址:GitHub https://github.com/rjpcomputing/luaforwindows/releases
附加笔记:因为1个汉字占两个字节,所以删除1个汉字需要删2次,只删掉或者选择1个字节时,会出现乱码.
2.LuaDist:虽是官方推荐,但是似乎没有IDE,所以不太常用,不过界面要高级许多,像Windows10原生的工具.
下载地址:官网 http://luadist.org/
三.与C#的区别
1.分号
C#
print("Hello World"); //每句结尾必须加";"
Lua
print("Hello World") --可以不加";"
print("Hello World"); --也可以加";",没区别
2.注释
C#
//单行注释
/*
多行注释
*/
Lua
--单行注释
--[[
多行注释
--]]
--取消多行注释小技巧:由多行注释转变成单行注释:在一开头或者结尾增加一个"-"
---[[单行注释
--]]单行注释
3.定义变量
C#
int i=10; //需要给一个int定义其类型
print(a); //报错"a为未知变量"
Lua
i=10 --不需要定义其类型,默认为全局变量
print(a) --因为不用定义其类型,所以出现一个新的变量就相当于已声明,若没有赋值的话,其值和类型为nil(意思为空)
--所以该行并不会像C#一样报错"a为未知变量",而是输出的打印值为nil
--关于表的定义与销毁
table={key0=0,key1=1} --声明并定义表类型
print(table.key0)
table.key0=nil --销毁key0
print(table.key0) --打印值为nil
table=nil --销毁table,释放table的内存
print(table.key0) --报错Error
//销毁一个变量后还能使用其名字,但这是一个新的变量了,所以原来的变量类型和其子值都不存在,能不嫩用子值还是要看有没有给他又定义了有子值的类型.
4.字符串
C#
//定义字符串
string str="This is a string."; //只能用双引号表示字符串
print("Print a string");
//转行:转行符
string str1="a\nb"; //"\n"为转行符,注意是\,不是/
//连接字符串
print(str1+"a"); //"+"可以连接字符串,输出This is a string.a
print(1+2); //"+"还可以进行算术运算,输出3
//获得字符串长度:字符串.Length
print(str.Length);
Lua
--定义字符串
str1='This is string1' --单引号/双引号都可以表示字符串
print('Print a string1')
str2="This is string2"
print("Print a string2")
--转行:多行字符串
str1="abc" --单行字符串
str2=[[ --多行字符串
First line. --第一行
Second line. --第二行
Third line. --第三行
]]
--连接字符串:..双点符号
print(str1..str2) --输出4行
print("a".."b") --输出ab
--运算符"+ - * /":会把数字字符转换成数字然后根据运算符进行运算(只限数字字符)
print("1"+"2") --输出3
print("1"+2) --输出3,支持混合运算,反正数字字符被当作数字来用
print("1"-2) --输出-1
print("2e2"*"6") --输出1200,2e2=200
print("2e2"/"10") --输出20
--获得字符串长度:在字符串前面加#井号,就变成了number类型的长度
print(#str1) --输出3
print(#"abcde") --输出5
print(#"你好ab") --输出6,一个汉字占两个字节
5.布尔数据类型
C#
bool b1=true;
Boolean b2=false; //Boolean数据类型依赖于命名空间"using System"
//一般用"关系运算符"或"逻辑运算符"连接起来的"关系表达式"或"逻辑表达式"都代表一个bool值,通常用来作为判断语句的条件
//关系运算符:> < == >= <= != 共6个
//逻辑运算符:|| && ! 共3个
if(b1==true) print("b1==true"); //b1==true就是关系表达式
b1=(1>0&&b2==false)?true:false; //1>0&&b2==false就是逻辑表达式(包括逻辑运算符就是逻辑表达式)
//除了"关系表达式"和"逻辑表达式",其他适用直接作判断条件的数据类型
if(0) print("0可以用来判断"); //报错,int类型0不能转换成bool类型
if(null) print("null可以用来判断"); //报错,null不能转换成bool类型
GameObject obj=null; //声明obj(未赋值)
if(obj) print("被赋值的物体可以用来判断"); //报错:obj为未赋值变量
obj=null; //给obj赋值为null
if(!obj) print("被赋值的物体可以用来判断"); //打印改行:因为obj=null,所以!obj=true
//总结:除了bool类型以外的常量类型,都不能转换为bool类型(也不能强制转换),所以不能用来作判断条件.
// 而GameObject类型的必须赋值后,才能作判断条件,除了=null时作判断条件为false,=其他值时都为true.
Lua
b=true --boolean类型只包含true和false两个值
if nil or false then
print('nil和false至少一个被当作true')
elsd
print('nil和false都被当作false') --false和nil被当作假
end
if 0 then
print('0被当作true') --除了nil和false被看作假,其他值包括0都被当作真
end
6.获取变量的类型
C#
//使用GetType()方法获取变量类型
int i=0;
print(i.GetType()); //打印:System.Int32
print(0.GetType()); //打印:System.Int32,常量也可以用该方法获取类型
Lua
--使用"type(变量名)"的方法
print(type(false)) --打印boolean
print(type(nil)) --打印nil
print(type(10)) --打印number
print(type(1.1)) --打印number
print(type(print)) --打印function
print(type(x)) --打印nil,未赋值的变量都为nil类型
7.数字类型
C#
//数值类型包括整数型和浮点型,无符号整型在基本类型前加一个"u",其只有非负值
sbyte = -1; //8位整形
short = -1; //16位短整型
int i = -1; //32位整形
long l = -1; //64位长整形
float f = -1.0f; //32位单精度浮点型,6~7位有效小数
double d = -1.0; //64位双精度浮点型,15~6位有效小数
//给无符号型变量一个负值,C#会报错(只有无符号整形,没有无符号浮点型)
byte = 1; //无符号8位整形
ushort = 1;
uint i2 = 1;
ulong l = 1;
//附加:C语言里,用unsigned int定义,赋值给unsigned类型一个负值,不会报错,可以根据补码的算法得到其实际值
Lua
--只有number类型,包括整数型和浮点型,以下打印出来全为number类型
print(type(2))
print(type(2.2))
print(type(0.2))
print(type(2e+1)) --e+1=10的1次方,即10
print(type(0.2e-1)) --e-1=10的-1次方,即0.1
print(type(7.82647392e-06)) --e-06即10的-6次方,小数点向左移动6位
8.字典与数组
C#
//C#需要new初始化一下,索引从0开始,Lua从1开始
//数组,长度不可变,数据类型只限定义的类型,元素中间可以有空值
int[] i1=new int[3];
int[] i2={0,1,2}; //数组长度为3,定义之后不能再这样批量赋值,例i1={7,8,9}是错误的
i1[0]=100;
Array.Copy(int[] array1,int[] array2,int length);//从array1的0索引元素开始,复制length个元素,然后从array2的0索引开始粘贴
Array.Copy(int[] array1,int index1,int[] array2,int index2,int length)
//从array1,index1索引元素开始,复制length个元素,然后从array2的index2索引开始粘贴
//字典,长度可变,数据类型只限定义的类型(关键字不能修改,只能修改值).
//需要"using System.Collections.Generic;"命名空间,"using System.Collections"不行
Dictionary<关键字的数据类型1,值的数据类型2> d=new Dictionary<数据类型1,数据类型2>();
Dictionary<string,int> d=new Dictionary<string,int>();
d.Values; //只包含字典的值的一维数组
d.Keys; //只包含字典的关键字的一维数组
d[关键字]=100; //关键字是string字符串类型
d.Add(关键字,值); //注意关键字和值的数据类型
d.ContainsKey(关键字); //是否包含指定的键,返回bool值
d.ContainsValue(值); //是否包含特定值,返回bool值
d.GetEnumerator(); //返回循环访问 Dictionary的枚举数,具体看下面"11遍历方法"
d.TryGetValue(关键字,out result);//获取与指定的键相关联的值
...
//ArrayList列表,长度可变,数据类型可有多种,都装箱存储为object类型,所以只是用动态数组的话还是比较推荐下面的List
ArrayList aL=new ArrayList();
aL[0]=100; //修改
aL.Add(要添加的对象); //增加,在末尾位置
aL.RemoveAt(键值); //删除,键值=0则删除第1个元素,后面的元素自动补空
aL.Contains(要判断是否在列表里的对象);//检查是否存在
aL.Insert(插入位置键值,要插入的对象); //插入,从该位置起所有元素后移一位,在该键值处插入该对象
aL.Clear(); //清空列表
aL.AddRange(批量对象); //批量增加
aL.RemoveRange(批量对象); //批量删除
aL.InsertRange(插入位置键值,批量对象);//批量插入
...
//列表,长度可变,数据类型只限定义的类型
List<int> l=new List<int>();
l[0]=100; //修改
l.Add(要添加的对象); //增加
l.Remove(要删除的对象); //删除,后面的元素自动补空
l.Contains(要判断是否在列表里的对象) //检查是否存在
l.Clear(); //清空列表
...
//Queue队列,先进先出,先不写占个位置
//.Length是他们的长度.Array是可以转换成数组的,但有限制;字典也可以转换成List,但GC较高.
Lua
--table表类型:可以当作字典使用,也可以当作数组使用,其长度不固定,大小也不确定.
--索引key数字类型的key和字符串类型的key混合存在.
--当key为数字类型的时候,是可以为负数的,例如arary[-1]=10.
--二维数组就是把一维数组的每个元素赋值为一维数组(表),多层以此类推
--第一种表:空表
tab1={} --{}构造表的符号,表示tab1的类型为表
print(tab1[1]) --可以用3种方法获得键值,但以为是空表所以值全是nil
print(tab1["key"])
print(tab1.key)
--第二种表:字典表,以(key=value)键值对的形式存储键值,key都是字符串类型的(不用加引号)
tab2={key1=100,key2="value2"} --初始化
print(tab2["key"]) --可以用2种方法获得键值
print(tab2.key)
--第三种表:数组表,只存储值,key(即关键字/索引)为数字类型默认值为1.2.3.4...(从1开始,C#从0开始),但是可以不连续
tab3={"apple",100,"orange","grade"} --初始化
print(tab3[1]) --可以用1种方法获得键值
tab3[1]=nil --销毁第一个键值对
--若是遍历打印,就会发现从第二个键值对开始打印(并不会打印1:nil哦)
--并且第二个键值对的key值还是2,不会变成1
--对表进行操作:
1.取值方法有3种(与空表一致):
tab1[1]) //键值为number类型
tab1["key"] //键值为字符串类型
tab1.key //键值为字符串类型
2.修改值:直接用上面的取值直接再赋值就行,可以随便再赋值其他数据类型的值.
--多维数组有两种定义方法
--直接赋值为多维数组
array1={{"小明","小红"},{"小刘","小狼"}}
--一维数组中每个元素都可以被赋值为一个一维数组
array2={}
for i=1,3 do
array2[i]={}
for j=1,2 do
array2[i][j]={}
for k=1,2 do
array2[i][j][k]=i*j*k
end
end
end
四.与C#面向对象的区别
C#
class c1 //类
{
public static void main(string[] args) //入口方法
{
}
}
Lua
--直接写,就可以运行,没有类的概念,但是也有其他方法来实现面向对象
--Lua是用table来实现类似于C#的类的
五.数据类型
数据类型 | 描述 |
nil | 只有值nil属于该类,表示一个无效值,在条件表达式中相当于false,赋值为nil则表示销毁数据并释放所占内存 |
boolean | 包括两个值:false和true.判断时,除了false和nil,其他值都被当作true处理(包括0). |
number | 表示双精度Double类型的实浮点数(包括整形和小数) |
string | 字符串由一对双引号或单引号来表示 |
function | 由C或Lua编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串.在Lua里,table的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表. |
1.thread(线程)
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
2.userdata(自定义类型):
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
六.函数
C#
int function(string str) //需要定义函数返回的数据类型和参数的数据类型
{
int i=0;
print(int.TryParse(str,out i)); //int.TryParse返回true/false,若str是数字字符,则true,否则false
return i; //函数返回的数据类型不为void,则必须有该返回语句
}
//引用function(string str)
int j=function("q"); //j=0,打印false
int k=function("1"); //k=1,打印true
Lua
函数的定义:
--Lua的函数()和方法都用end来控制范围(C#用花括号"{}"控制范围,Lua的花括号"{}"表示构造table表类型)
--函数类型/函数参数/变量都不用声明类型,返回值可有可没有
--格式:
[local]function 函数名(参数1,参数2...)
执行语句1
执行语句2
......
return 返回值1,返回值2...
end
--[local]修饰符,如果是局部参数则加,否则不加.
--不需要定义函数返回的数据类型
--不需要定义参数的数据类型,return语句可以有可以没有,return语句可以同时返回1~多个返回值(示例看"八.多变量同时赋值").
--示例:
function func1(n) --函数用"函数头+end"来控制函数范围
print(n)
if n>1 then --判断:"if 判断条件 then 执行语句 end"
return n
end
end
--调用(func1(2)就是调用)
print(func1(2)) --输出两行2
--函数作为值赋给其他的变量(函数是一种数据类型)
function func1(n)
print(n)
if n>1 then
return n
end
end
--赋值
func2=func1 --现在func2是和func1一样的函数
--调用
print(func2(2)) --输出两行2
print(func2(1)) --输出1,第二行输出空(不是nil,是啥都没有)
print(func2("a")) --报错,因为上面用了与数字类型的比较,也体现了Lua数据使用方法的不安全性
--函数做参数(相当于C#里的委托和事件,具体看这篇https://blog.youkuaiyun.com/qq_29847807/article/details/103755321).
tab={key2="val1",key="val2"}
function f1(k,v)
print(k..":"..v)
end
function testFun(tab,fun) --函数作参数
for k,v in pairs(tab) do --复习遍历方法:for 变量1,变量2 in pairs(表) do 执行语句 end
fun(k,v)
end
end
testFun(tab,f1) --引用函数
--运行的时候,传入的实参不能有nil,所以函数testFun和其参数tab/f1都要在引用之前定义赋值.
--匿名函数:适合"只用一次"且"操作语句少"的函数.直接在引用里定义匿名函数.
--没有函数名所以不能被别的地方引用,但是可以把它赋值给新变量,就相当于有了函数变量名,就可以被--引用了.
tab={key2="val1",key="val2"}
function testFun(tab,fun)
for k,v in pairs(tab) do
fun(k,v)
end
end
--匿名函数作参数
testFun(tab, --引用函数
function (k,v) --直接在引用里定义匿名函数.
print(k..":"..v)
end)
--匿名函数作为值赋给其他变量(相当于有了变量名)
myprint=function(param)
print("这是我的打印函数"..param)
end
myprint(100) --调用函数
七.全局变量/局部变量,生命周期,制造语句块(控制局部变量生命周期)
C#
public int i_1; //外部可访问的全局变量
private int i_2; //本文件内可访问的全局变量
void Fucntion()
{
int i_3; //fuction内的局部变量
}
//这里先不罗列static变量了
Lua
--全局变量
a=5 --默认全局
print(type(a))
a="Hello" --可以随意赋值其他数据类型的值
print(type(a))
--局部变量:访问局部变量比全局变量快,更节约内存.
local b=10
print(b)
--在语句块内,如果你用系统自带的变量名字(自动接收数据),命名新的局部变量,则整个语句块内的该变量以你定义的局部变量为准
function test(...) --如果没有再定义arg的话,系统默认arg={...,n=参数数量}
for k,v in pairs(arg) do --pairs()括号内不能为nil,且类型必须键值对
print(k,v)
end
local arg={...} --这里再次定义arg变量(有等号就是重新定义)获取{...}(不包括n=参数数量了),那么上面的arg则是只声明但是没有定义的变量,所以导致pairs()内的值是nil然后报错.正确的做法是在语句块的开头"抢占"该名字.
print(arg)
end
test(10) --调用函数
--生命周期(从创建到销毁,也可以称为有效范围)
--全局变量生命周期:全局
--局部变量生命周期:语句块内(若在文内定义局部变量,则全文内有效)
function test()
c=5 --在函数内声明全局变量,在函数外也有效
local d=6 --d的声明周期到该函数的语句块end位置结束
end
print(c,d) --nil nil.若不调用函数,则函数语句块不执行
test()
print(c,d) --5 nil
--制造语句块:do 执行语句块 end. (可用来控制局部变量生命周期)
--自创语句块:do 执行语句块 end.会自动执行
do
local a=10 --局部变量与全局变量重名,语句块内以局部变量为准
b=11
print(a,b) --10 11
end
print(a,b) --Hello 11
八.多变量同时赋值
C#
//只能同种类型的变量,并且要赋相同的值时才能多变量一起赋值
int j,k;
j=k=1;
print(j+" "+k); //打印要自己拼字符串
Lua
--多个值同时处理时,用","逗号隔开
a,b=10,20 --不是a=10,b=20;而是先得到右边所有的值,然后一起赋给左边的值
a,b=b,a
print(a,b) --20 10(而不是20 20)
a,b,c=11,21,"Hello" --可以赋不同类型的值
a,b=10,20,30 --30自动被省略
a,b,c=10,20 --c被赋值为nil
--函数可以返回多个值(赋给多个变量)
function test()
return 40,50 --返回多个值
end
a=test()
print(a) --40
a,b=test()
print(a,b) --40 50
九.循环
C#
包括while/foreach/for/do..while四种方法.可以互相嵌套.
//foreach遍历格式:
Dictionary<string,int> dic = new Dictionary<string,int>();
dic.Add("1",100);
foreach(KeyValuePair<string,int> pair in dic)
{
print(pair);
}
//for方法:foreach会产生更多GC,所以还是推荐用for方法.
//for方法可以代替所有其他的方法,并且可以缩减代码量到for(初始设置;循环条件;执行内容)一行中.
for(int i;i<10;i++) print(i);
//while方法:
Dictionary<string,int> dic=new Dictionary<string,int>();
dic.Add("key1",0);
var enumerator=dic.GetEnumerator();
while(enumerator.MoveNext()) //MoveNext把enumerator指向下一个键值对
{
print(enumerator.Current.Key); //打印当前的key
print(enumerator.Current.Value); //打印当前的Value
}
//do..while方法:最后的";"分号别忘了
do
{
}while(循环条件);
Lua
包括while/数值for/泛型for/repeat until4中循环.可以互相嵌套.
--while循环:while(循环条件)do 执行语句 end
a=1
while(a<=20) --()括号可加可不加,推荐加上
do --do也可以另起一行
if(a%2==1) then --加减乘除似乎和C#用法一样
print(a)
end
a=a+1 --没有自增自减方法++/--,因为--是注释嘛
end
--数值for循环:for i=start,end,step do 循环体 end
for i=1,10 do --i从1自增到10,默认每次+1
print(i)
end
for i=20,1,-3 do --i从20自减到1,每次-3
print(i)
end
--泛型for循环
tab1={key1="val1",key2="val2"}
for k,v in pairs(tab1) do --相比C#,不是用foreach而是用for,少了()和{},多了do和and关键字
print(k,v)
end
tab2={"apple","banana",1,2,3}
for k,v in pairs(tab2) do --pairs()是一个方法,用来取一对儿值
print(k,v)
end
--repeat until(相当于C#的do..while):repeat 循环体 until (停止循环条件).先执行一次再开始判断
a=1
repeat
print(a)
a=a+1
until(a>10) --不需要end
--循环嵌套例子:
--for/while嵌套
for i=1,10 do
j=1
while(j<=i)do
print(i)
j=j+1
end
end
十.流程控制(if判断语句)
C#
//if(bool)...else if(bool)....else
int a=50;
if(a>50)
print("a>50");
else if(a<50)
print("a<50");
else
print("a=50");
Lua
--if(布尔表达式)then...elseif(布尔表达式)then...else...end
--布尔表达式的"()"可加可不加,C#必须加,Lua还多了"then"和"end".
--elseif中间没有空格
if(not a)then --C#的!/&&/||逻辑运算符在lua中为not/and/or
print("a不存在")
elseif(a<=100)then
print("a<=100")
elseif(0)then --除了nil和false代表假,其他都代表真,0也代表真
print("0被当作true")
else
print("a>=100and0被当作false")
end
十一.可变参数(参数数量可变)
C#
C#的函数定义了几个参数就只能传入几个参数,参数数量不可变.若非要用不定数量的参数,只能借助于长度可变的List/ArrayList/Dictionary等数据类型.
Lua
--可变参数:指参数的数量可变
--例如print()函数,可以有任意个数参数,逗号隔开
--定义可变参数的函数:funcyion 函数名(...) 执行参数 end
function average(...) --系统默认arg={...,n=参数数量}
local arg={...} --加不加Local都可以,推荐加上.这步是重新定义(=)使arg不包括n=参数数量,以方便数值的批处理
sum=0
for k,v in pairs(arg) do
sum=sum+v
end
print(sum/#arg)
end
--可以传入不同个参数
average(10)
average(10,30)
average(10,30,50,80)
十二.算术运算符,关系运算符,逻辑运算符
C#
数学运算符:+ -(减/负) * / %5种,平方需要用数学库Mathf.Pow(底数,指数).
关系运算符:> < >= <= == !=6种,C#的~是按位取反符.
逻辑运算符:&& || ! 3种.
其他运算符:连接字符串用 "+" 号 ,获取长度则是访问属性".Length" .
Lua
--数学运算符:+ - * / % ^,加 减(负) 乘 除 余 幂
a=6
b=2
print(a+b)
print(a-b)
print(-a)
print(a*b)
print(a%b)
print(a^b)
--关系运算符:== ~= >= <= > <,关系表达式的值为boolean类型的(C#的"非等于"是!=,Lua是~=)
a=3
b=4
print(a==b)
print(a~=b)
print(a>=b)
print(a<=b)
print(a>b)
print(a<b)
--逻辑关系符:and or not,并 或 非
a=true
b=false
print(a and b)
print(a or b)
print(not a)
--其他运算符:.. # ,连接字符串 获取长度
print("Hell".."o") --".."字符串连接运算符
print(#"12345") --"#"获取字符串长度,还能获取表的最大的数字key(会把字符串key忽略掉)
tab={0,2}
tab[4]=2
tab.key1=4
tab.key2=5
print(#tab) --打印4
十三.转义字符
C#
\n \\ \" \',其他查看下面Lua的链接,基本上转义字符都差不多.
Lua
--常用转义字符
print("a\nb") --\n 换行
print("D:\\UnityProjects\\Test") --\\ 代表 \,一般写地址字符串时要用到
print("\"a\"") --\" 代表 “
print('\'a\'') --\' 代表 ‘
print("'a'") --里外引号不同可以不用转义
print('"a"')
--其他转义字符查看https://www.runoob.com/lua/lua-strings.html
十四.字符串常用操作函数
C#
查看"菜鸟教程":https://www.runoob.com/csharp/csharp-string.html的"String 类的方法".
Lua
--其他格式字符可以查看菜鸟教程:https://www.runoob.com/lua/lua-strings.html的"字符串格式化"
--字符串操作:例如string.upper(字符串),string是个表(但实质上实现了面向对象更像C#中的类),upper为函数类型
str="Hello World"
print(string.upper(str)) --返回都变大写的字符串
print(string.lower(str)) --返回都变小写的字符串
print(string.gsub(str,"or","123",3)) --返回替换了的字符串,把"or"替换成"123",最多替换3个(可以不指定),字母区分大小写
print(string.find(str,"l",6)) --返回第1个l“的索引位置,从第6个索引位置开始寻找(可以不指定),搜索不到就为nil
print(string.reverse(str)) --返回反转了的字符串
print(string.char(97,98,99,100)) --返回根据ASCII码转换数字成的字符,可接受多个数字逗号隔开
print(string.byte("abcd",4)) --返回第4个字符的数字类型的ASCII码,可以不指定4,则默认是第一个字符
print(string.len("abcd")) --返回字符串长度,功能#"abcd"
print(string.rep("abcd",2)) --返回复制2次并连接成的新的字符串
str="year"
num1=987
print(string.format("%s=%04d",str,num1))
--格式化,组拼语句,%04d表示保留4位,前面的空位补0,若没有0则空着位置.与C语言的输出方法printf类似
print(string.match("Hello Lua user.","%a+"))
--返回第一个符合"%a+"的一串字符,正态表达式pattern除了可以是"%a+",也可以是单个的字符常量比如"a"
for word in string.gmatch("Hello Lua user.","%a+") do
print(word)
end
--string.gmatch("Hello Lua user.","%a+")返回没有索引key的一维数组,每个元素都符合"%a+"(指一串连续的字母),这里返回一个个单词
--"%a+"是一个正态表达式pattern,表示一个规则,一般会在注册的时候检测输入内容格式是否正确
--要看更多关于正态表达式pattern的内容,可以查看菜鸟教程https://www.runoob.com/lua/lua-strings.html的"匹配模式"