lua_乱码 - CSDN
lua 订阅
Lua [1]  是一个小巧的脚本语言。它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人所组成的研究小组于1993年开发的。 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。 [2]  Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。 [2]  一个完整的Lua解释器不过200k,在所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择。 [2] 展开全文
Lua [1]  是一个小巧的脚本语言。它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人所组成的研究小组于1993年开发的。 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。 [2]  Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。 [2]  一个完整的Lua解释器不过200k,在所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择。 [2]
信息
类    型
脚本语言
研究时间
1993年
外文名
lua
研究地点
巴西里约热内卢天主教大学
lua保存和运行
运行可以通过 Lua 的交互模式,也可以用记事本编辑代码保存为 .lua 的格式,通过 Lua 编译器运行。也可以通过第三方工具,将 Lua 打包独立运行。
收起全文
精华内容
参与话题
  • Lua语言从入门到精通

    万人学习 2018-10-22 21:38:05
    深入浅出Lua学习
  • Lua基础(一)

    千次阅读 多人点赞 2019-01-25 22:35:55
    目录Lua是什么Lua应用场景Lua和C#的区别print方法、单行/多行注释Lua命名规则(标识符)全局变量的使用和销毁Lua中的数据类型nil 空类型boolean 布尔类型number数字类型string字符串类型table表类型function函数...

    Lua是什么

    Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
    Lua 可以很方便的和其他程序进行集成(c++,c#,java等)


    Lua应用场景

    游戏开发
    独立应用脚本
    Web 应用脚本
    扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
    安全系统,如入侵检测系统


    Lua和C#的区别

    Lua 可以在几乎所有的操作系统和平台进行编译运行,可以很方便的更新代码,更新了代码后,可以直接在手机上运行,不需要重新安装(比如热更新方案)

    C#只能在特定的操作系统中进行编译成dll文件,然后打包进安装包在其他平台(比如 Android、iOS),如若已经运行在移动平台上,不能更新替换已有的dll文件,除非重新下载安装包


    print方法、单行/多行注释

    print("Hello world")
    
    -- 用来表示单行注释
    
    --[[
    用来表示多行注释
    --]]
    
    

    Lua命名规则(标识符)

    1,不能以数字开头
    2,不能是关键字
    关键字比如:
    and//break//do//else//elseif//end//false//for//function等

    而且在 Lua 中大小写是敏感的,and 是关键字,And,AND 这是两个不同的标识符

    还有_XXX(_ABC)这种不推荐使用(是保留用法,在 Lua 中有特殊的用法)


    全局变量的使用和销毁

    Lua中变量不需要声明,可以直接使用,也不需要初始化,可以直接使用。
    对于没有初始化和声明的变量默认值为 nil(空类型,空值)

    b = nil
    

    把一个变量置空(nil),相当于没有使用过这个变量,Lua 会销毁 b 所占的内存


    Lua中的数据类型

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

    在 Lua 中有一个 type 方法,它可以返回数据的类型,输出的是 string 字符串,比如:

    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 值
    把一个变量赋值为 nil,表示回收并删除这个变量


    boolean 布尔类型

    这个下面只有两个值 true 和 false
    false 和 nil 都代表 false,其他任何值都代表 true(0和空字符串 Lua 也都认为是 true)


    number数字类型

    1,数字的表示方式
      4  0.4   4.45e-3  0.3e12  5E+20
    2,整数和小数都是 number 类型的
      type(3)    type(3.5)
    3,如果整数和小数的大小是一样的,那么 Lua 认为就是相等的
      1 == 1.0   -3 == -3.0  0.2e3==200
    4,如果我们想要区分整数和小数这两种类型,可以使用 math.type 函数
      math.type(3)  math.type(3.0)  integer  float
    5,1e3 表示一个小数
      1000.0
    6,支持16进制
      0xff  0x1A3  0x0.2


    string字符串类型

    定义:使用单引号或者双引号表示字符串

    1,替换字符串

        a="one string";
        b=string.gsub(a,"one","another")
        print(a)     --> one string
        print(b)     --> another string
    

    2,取得字符串的长度#

        print(#a)     --> 10
        print(#"good bye")     --> 8
    

    3,字符串组拼

        "Hello".."World"
        "result is"..3
    

    table表类型

    表在 Lua 中是一个非常重要的数据结构,也是最有权力的。
    1、我们可以使用表表示数组,集合,字典…
    2、Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
    3、Lua table 是不固定大小的,你可以根据自己需要进行扩容。
    4、表既不是值类型也不是变量,它是对象。
    5、Lua 也是通过 table 来解决模块(module)、包(package)和对象(Object)的。 例如 string.format 这里其实 string 并不是一个类。在 Lua 中没有类,Lua 中使用 table 实现类、面向对象这些概念。

        tab1 = {} --空表   {}构造表达式
        
        tab2 = {key1=100,key2="value2"} --初始化一个表
       
        tab2[1] = "Lua"   -- 指定值
        
        print(tab1)
        print(tab1.key)
        
        print(tab2.key1)
        print(tab2["key1"])
        
        tab2 = nil  -- 移除引用 Lua 的垃圾回收会释放内存
        
        tab3 = {"apple","pear","orange","grape"}
        
        print(tab3[2])
        print(tab3["2"])
        
        for key,val in pairs(tab3) do
        	print(key..":"..val)
        end
        
        for key,val in pairs(tab2) do
        	print(key..":"..val)
        end
    

    关于表的自动垃圾回收:当我们创建一个表 a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向 a,Lua的垃圾回收机制会清理相对应的内存。


    function函数

    比如一个求阶乘的函数

    function fact(n)
    	if n==1 then
    		return n;
    	else
    		return n*fact(n-1);
    	end
    end
    
    print(fact(3))
    print(fact(5))
    
    fact2 = fact
    print(fact2(5))
    

    另一种,关于function函数,作为参数传递和匿名函数的用法

    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和userdata类型

    在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
    线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

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


    全局变量和局部变量的声明和使用

    全局变量

    a = 5 
    print(type(a))   --> number
    --类型可变
    a = "Hello"
    print(type(a))   --> string
    

    局部变量

    --局部变量的销毁是在所在语句块结束
    local b = 10
    print(b)
    
    function test()
    	c = 5
    	local d = 6
    end
    
    test()
    
    print(c,d)
    
    do
    	local a = 10
    	b =11
    	print(a,b)
    end
    
    print(a,b)
    

    多变量赋值

    a,b = 10,20   -- a = 10 b = 20
    
    a,b,c = 10,20,"Hello"
    print(a,b,c)
    
    a,b = b,a   -- a = b  b = a 也就是 变量交换值
    print(a,b)
    
    a,b = 10,20,30   --多的值自动略去
    print(a,b)
    
    a,b,c = 20,30   --没有值赋值 nil
    print(a,b,c)
    
    function test()
    	return 40,50
    end
    
    a,b = test()   --返回多个值 并获取
    print(a,b)
    
    

    循环

    1,while循环
    2,for循环
    3,repeat unitl (do while)


    while循环

    while (condition) 
    do
    	statements
    end
    
    a = 1
    while (a<=20) 
    do
    	if (a%2==1) then
    		print(a)
    	end
    	a=a+1
    end
    

    for循环

    1,数值for循环

    for var=start,end,step do
    循环体
    end
    这里var会从start变化到end,每次变化一step进行

    for i=1,10,2 do
    	print(i)
    end
    
    for i=20,1,-3 do
    	print(i)
    end
    

    2,泛型for循环

    tab1 = {key1="value1",key2="value2"}
    
    for k,v in pairs(tab1) do
    	print(k,v)
    end
    
    tab2 = {"apple","橡胶","西瓜","猕猴桃"}
    
    for k,v in pairs(tab2) do
    	print(k,v)
    end
    

    repeat until循环

    repeat
    循环体
    until(condition)

    a = 1
    repeat
    	print(a)
    	a=a+1
    until(a>10)
    

    循环嵌套

    for i=1,10 do
    	for j=1,i do
    		print(i)
    	end
    end
    
    
    for i=1,10 do
    	j=1
    	while j<=i do
    		print(i)
    		j=j+1
    	end
    end
    

    流程控制

    if (布尔表达式) then
    为true的时候要执行的代码
    end

    if(布尔表达式)then
    为true的时候要执行的代码
    else
    为false的时候要执行的代码
    end

    if (布尔表达式) then
    //1
    elseif (布尔表达式) then
    //2
    else
    //3
    end

    -----
    if (0) then
    	print(0)
    end
    -----
    a  = 10
    if (a>10) then
    	print("a大于10")
    end
    -----
    if a<=10 then
    	print("a小于等于10")
    end
    -----
    if (b) then
    	print("b不为空")
    else
    	print("b为空")
    end
    -----
    a = 100
    if (a<=50) then
    	print("a<=50")
    elseif (a<=100) then
    	print("a<=100")
    elseif (a<=150) then
    	print("a<=150")
    else
    	print("上面三种情况都不满足")
    end
    
    

    function用法特性总结

    [local] function functionName(arg1,arg2,arg3…argn)
              functionBody
              [return value1,value2…valuen]
    end

    local function max(num1,num2)
    	if(num1>num2)then
    		return num1
    	else
    		return num2
    	end
    end
    
    print( max(1,10) )
    
    --函数可以作为数据赋值   可以作为参数传递
    temp = max
    print(temp(40,3))
    -----
    myprint = function (param)
    	print("这个是我的打印函数"..param)
    end
    
    myprint(100)
    -----
    function add(num1,num2,printFun)
    	local res = num1+num2
    	printFun(res)
    end
    
    add(40,50,myprint)
    
    --lua里面的函数可以返回多个值
    

    可变参数 用 ... 三个点表示

    function average(...)
    	local arg = {...}
    	res = 0
    	for k,v in pairs(arg) do
    		res = res+v
    	end
    	-- #arg 取得参数的个数  	#"hello"
    	print(res/#arg)
    end
    
    average(10)
    average(1,30)
    average(3,8,90)
    average(5,67,7,8,3)
    

    运算符

    算术运算符:+ - * / % ^求幂 -

    关系运算符: == ~= > < >= <=

    
    if(a==b)then
    	print("a==b")
    else
    	print("a~=b")
    end
    -----
    if(a~=b)then
    	print("ab不相等")
    else
    	print("ab相等")
    end
    -----
    if(a<b)then
    	print("a小于b")
    else
    	print("a不小于b")
    end
    

    逻辑运算符 and or not
    a and b a ,b 都为true则结果为true
    a or b a,b中只要有一个为true,结果为true
    not a 非/取反

    print( 30>20 and 10>30)
    
    print( false or false  )
    
    print (not true)
    

    字符串操作

    str ="My name is MoGu! name"
    str2 =string.upper(str)
    str3 =string.lower(str)
    
    str4 =string.gsub(str,"i","123",5)
    
    index = string.find(str,"name",5) --返回所查找到的位置的索引
    
    str5=string.reverse(str)
    
    num1 = 5
    num2 = 10
    
    print(str,str2,str3,str4,index)
    print(str5)
    print("加法运算:"..num1.."+"..num2.."="..(num1+num2))
    username = "w3er4wwrfwer"
    password = "lkjlw3e4rl"
    print("select * from user where username = '"..username.."' and password ='"..password.."'")
    
    str6=string.format("加法运算:%d+%d=%d",num1,num2,(num1+num2))
    print(str6)
    str7 = string.format("select * from user where username='%s' and password ='%s'",username,password)
    print(str7)
    
    date = 2; month = 1; year = 2014
    print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
    
    s1 = string.char(97,98,99,100)
    i1 =string.byte("ABCD",4)
    i2 =string.byte("ABCD")
    print(s1,i1,i2)
    
    length1 = string.len("abc")
    length2 = #"abc"
    print(length1,length2)
    
    s2=string.rep("abcd",4)
    print(s2)
    
    for word in string.gmatch("Hello Lua user", "%a+") do
    	print(word)
    end
    

    数组

    Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。

    array = {"Lua","C#"}
    array[3]="Java"
    
    for i=1,3 do
    	print(array[i])
    end
    
    array = {}
    
    for i =-2,2 do
    	array[i]=i*3
    end
    
    for i=-2,2 do
    	print(array[i])
    end
    
    

    二维…多维数组

    array = { {"小明","小红"},{"小刘","小狼"},{"大明","大刘"},{"小赵","李四"} }  --4*2
    
    print(array[3][1])
    
    for i =1,4 do
    	for j=1,2 do
    		print(array[i][j])
    	end
    end
    
    array = {{},{},{}}
    
    for i = 1,3 do
    	array[i]={}
    	for j=1,2 do
    		array[i][j]=i*j
    	end
    end
    
    for i = 1,3 do
    	for j=1,2 do
    		print(array[i][j])
    	end
    end
    
    

    迭代器

    pairs迭代table,遍历表中所有的key跟value
    ipars按照索引从1开始,递增遍历,遇到nil值就停止

    array = {"Lua","C#","Java"}
    
    for k in pairs(array) do
    	print(k,v)
    end
    
    array[2]=nil
    
    for k,v in ipairs(array) do
    	print(k,v)
    end
    

    整体的结构

    for 变量列表 in 迭代函数,状态变量,控制变量  do
    	--循环体
    end
    

    1,调用迭代函数,(把状态变量和控制变量当做参数传递给迭代函数) 状态变量只会在第一次调用的时候赋值。
    2,如果迭代函数的返回值为nil,退出for循环;如果不是nil的话,把返回值赋值给变量列表,并执行循环体。

    自定义迭代函数,求平方

    function square(state,control)
    	if(control>=state) then
    		return nil
    	else
    		control=control+1
    		return control,control*control
    	end
    end
    
    for i,j in square,9,0 do
    	print(i,j)
    end
    

    表的基本

    mytable = {}
    
    mytable[1] = "Lua"
    
    mytable[1] = nil
    
    mytable = nil
    
    mytable = {}
    
    print( type(mytable) )
    
    mytable[1] = "Lua"
    mytable["name"]="siki"
    
    newtable = mytable
    
    print(newtable[1])
    print(mytable[1])
    
    newtable[1]="C#"
    
    print(newtable[1])
    print(mytable[1])
    
    newtable[2]="Java"
    
    print(mytable[2])
    
    
    mytable = nil
    
    print(mytable.name)
    print(newtable.name)
    
    newtable = nil
    

    表的基本操作

    --方法一般都是 table.xxxxmethod
    
    --Table连接
    mytable = {"Lua","C#","Java","C++","C","abc","ABC"}
    
    print( table.concat(mytable) )
    print( table.concat(mytable,",") )
    print( table.concat(mytable,",",2,4) )
    
    --Table插入   mytable[6]="PHP"
    mytable[#mytable+1]="PHP"
    print(mytable[#mytable])
    table.insert( mytable,"Javascript" )
    print(mytable[#mytable])
    table.insert(mytable,2,"Boo")
    print(mytable[2],mytable[3])
    
    --Table移除
    mytable[2]=nil
    print(mytable[2])
    table.remove(mytable,2)
    print(mytable[2])
    
    --Table排序
    mytable={34,32,34,2,45,45,435,6,4576,76,33,23,24,2343,21,2,2,2,2,2,2,2,2}
    
    print("排序前")
    
    for k,v in ipairs(mytable) do
    	print(k,v)
    end
    
    table.sort(mytable)
    print("排序后")
    
    for k,v in ipairs(mytable) do
    	print(k,v)
    end
    
    --自定义最大值方法
    function get_max_number(tab)
    	local mn = 0
    	for k,v in pairs(tab) do
    		if(mn<v) then
    			mn=v
    		end
    	end
    	return mn
    end
    
    print(  get_max_number(mytable) )
    
    

    模块

    Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 。

    module = {}
    
    module.var = "Name"
    
    module.func1 = function ()
    	print("这个是Module里面的函数")
    end
    
    function module.func1()
    	print("这个是Module里面的函数")
    end
    
    local function func2()
    	print("这个是局部函数fun2")  --相当于一个私有函数 private
    end
    
    function module.func3()
    	func2()
    	print("这个是全局函数func3")
    end
    
    return module
    
    

    那么如何使用它呢:

    --require "模块名"
    --require ("模块名")
    
    m = require "module"
    
    print(m.var)
    m.func1()
    m.func3()
    

    元表(Metatable)

    元表提供了让我们改变 table 的行为的能力,每个行为关联了对应的元方法,可理解为对表操作的扩展。

    mytable = {"Lua","Java","C#","C++"} --普通表
    mymetatable = {} --元表   元表扩展了普通表的行为
    
    mytable =setmetatable(mytable,mymetatable)
    print( mytable[3] )
    
    print(getmetatable(mytable))
    print(mymetatable)
    
    tab = setmetatable({"Lua","Java","C#","C++"} , {__metatable="lock"} )
    print(getmetatable(tab))
    -- 使用__metatable可以保护元表,禁止用户访问元表中的成员或者修改元表。
    

    __index 元方法

    Lua 在查找一个表元素时的规则,是如下3个步骤:
    1.在表中查找,如果找到,返回该元素,找不到则继续。
    2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
    3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。

    mytable = {"Lua","Java","C#","C++"}
    
    --__index 当访问到一个表里不存在的索引的时候 起作用
    mymetatable = {
    __index = function (tab,key)
    	if(key>=10) then
    		return "Javascript"
    	end
    end
     }
    
    mytable =setmetatable(mytable,mymetatable)
    
    print(mytable)
    print(mytable[1])
    print(mytable[9]) 
    
    mytable = {"Lua","Java","C#","C++"}
    
    newtable = {seven="Javascript"}
    newtable[9]="C"
    
    mymetatable = {
    __index = newtable
     }
    
    mytable =setmetatable(mytable,mymetatable)
    
    print(mytable)
    print(mytable[1])
    print(mytable.seven)
    print(mytable[9]) 
    

    __newindex 元方法

    这个方法用来对表更新。当表设置了元方法 __newindex,在对新索引键赋值时,会调用元方法,而不进行赋值。而如果是对已存在的索引键,则会进行赋值,不调用元方法 __newindex。

    mytable = {"Lua","Java","C#","C++"}
    
    mymetatable = {
    __newindex = function(tab,key,value)
    	print("我们要修改的key为:"..key.." 把这个key值修改为:"..value)
    	rawset(tab,key,value)
    end
     }
    
    mytable =setmetatable(mytable,mymetatable)
    
    mytable[1]="C#"
    mytable[5]="Lua"
    
    print(mytable[1])
    print(mytable[5])
    
    mytable = {"Lua","Java","C#","C++"}
    
    newtable = {}
    
    mymetatable = {
    __newindex = newtable
    }
    
    mytable =setmetatable(mytable,mymetatable)
    
    mytable[1]="C#"
    mytable[5]="Lua"
    
    print(mytable[1])
    print(mytable[5])
    print(newtable[5])
    

    为表添加操作符

    相加操作,__add 操作符

    mytable = {"Lua","Java","C#","C++"} --普通表
    
    mymetatable = {
    __add = function(tab,newtab)
    	local mi = 0
    	for k,v in pairs(tab)do
    		if(k>mi) then
    			mi = k
    		end
    	end
    
    	for k,v in pairs(newtab) do
    		mi=mi+1
    		table.insert(tab,mi,v)
    	end
    	return tab
    end
    } --元表   元表扩展了普通表的行为
    
    mytable =setmetatable(mytable,mymetatable)
    newtable = {"PHP","Python"}
    
    v=mytable+newtable
    v2=newtable + mytable
    
    for k,v in pairs(v2) do
    	print(k,v)
    end
    

    __call元方法

    在表调用一个值时调用

    mytable = {"Lua","Java","C#","C++","ccdd"} 
    
    mymetatable = {
    __call = function (tab,arg1,arg2,arg3)
    	print(arg1,arg2,arg3)
    	return "MoGu"
    end,
    }
    
    mytable =setmetatable(mytable,mymetatable)
    
    v = mytable(123,34,453)
    
    print(v)
    

    __tostring 元方法

    用于修改表的输出行为,可以自定义表的输出内容
    示例一:

    mytable = {"Lua","Java","C#","C++","ccdd"} --普通表
    
    mymetatable = {
    __tostring = function (mytable)
    	local str = ""
    	for k,v in pairs(mytable) do
    		str = str..v..","
    	end
    	return str
    end
    }
    
    mytable =setmetatable(mytable,mymetatable)
    
    print(mytable)
    

    示例二:

    mytable = { 10, 20, 30} 
    
    mymetatable = {
    __tostring = function(mytable)
        sum = 0
        for k, v in pairs(mytable) do
            sum = sum + v
        end
        return "表所有元素的和为 " .. sum
      end
    }
    
    mytable =setmetatable(mytable,mymetatable)
    
    print(mytable)
    

    协同程序(coroutine)

    --定义协同函数coroutine.create
    --启动协同函数coroutine.resume
    --暂停协同函数coroutine.yield
    --继续运行 coroutine.resume (不需要传递参数)
    
    co=coroutine.create(
    	function (a,b)
    		print(a+b)
    		print(coroutine.status(co))
    		print(a+b)
    		print(coroutine.status(co))
    		print( coroutine.running() )
    		coroutine.yield(a*b,a/b)
    		print(a-b)
    
    		return a%b,a/b+1
    	end
    )
    print( coroutine.running() )
    print(coroutine.status(co))
    
    res1,res2,res3 = coroutine.resume(co,10,40)
    
    print(res1,res2,res3)
    
    print(coroutine.status(co))
    print("I'm here!")
    
    res1,res2,res3 = coroutine.resume(co)
    
    print(res1,res2,res3)
    
    print(coroutine.status(co))
    
    --第一个yield的参数作为第一个resume的返回值
    --第一个resume的参数作为协程的参数, 第二个resume的参数作为第一个yield的返回值
    
    
    function foo (a)
        print("foo 函数输出", a)
        return coroutine.yield(2 * a) -- 返回  2*a 的值
    end
     
    co = coroutine.create(function (a , b)
        print("第一次协同程序执行输出", a, b) -- co-body 1 10
        local r = foo(a + 1)
         
        print("第二次协同程序执行输出", r)
        local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入
         
        print("第三次协同程序执行输出", r, s)
        return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
    end)
            
    print("main", coroutine.resume(co, 1, 10)) -- true, 4
    print("--分割线----")
    print("main", coroutine.resume(co, "r")) -- true 11 -9
    print("---分割线---")
    print("main", coroutine.resume(co, "x", "y")) -- true 10 end
    print("---分割线---")
    print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
    print("---分割线---")
    

    Lua中文件的IO

    简单模式:拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
    完全模式:使用外部的文件句柄来实现。它以一种面向对象的形式,将所有的文件操作定义为文件句柄的方法。

    打开文件操作语句如下:
    file = io.open (filename ,[ mode])

    模式诸如以下这些:
    r,以只读方式打开文件,该文件必须存在。
    w,打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
    a,以附加的方式打开只写文件。若文件不存在,建立该文件,如果存在,写入的数据会被加到文件尾,即原先的内容会保留。
    r+,以可读写方式打开文件,该文件必须存在。
    w+,打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
    a+,与a类似,但此文件可读可写
    b,二进制模式,如果文件是二进制文件,可以加上b
    +,号表示对文件既可以读也可以写

    简单模式

    -- 以只读方式打开文件
    file = io.open("data.lua", "r")
    
    -- 设置默认输入文件为 data.lua
    io.input(file)
    
    -- 输出文件第一行
    print(io.read())
    
    -- 关闭打开的文件
    io.close(file)
    
    -- 以附加的方式打开只写文件
    file = io.open("data.lua", "a")
    
    -- 设置默认输出文件为 data.lua
    io.output(file)
    
    -- 在文件最后一行添加 Lua 注释
    io.write("--  data.lua 文件末尾注释")
    
    -- 关闭打开的文件
    io.close(file)
    
    file = io.open("data1.txt","w")
    io.output(file)
    io.write("www.runoob.com1\n")
    io.write("www.runoob.com2")
    io.write("www.runoob.com3")
    io.write("www.runoob.com4")
    io.close(file)
    
    
    file = io.open("data1.txt","r")
    io.input(file)
    print(io.read(10)) --读取一行\
    print(io.read(10)) --读取一行\
    print(io.read(10)) --读取一行\
    print(io.read(10)) --读取一行\
    print(io.read(10)) --读取一行\
    io.close(file)
    

    完全模式

    file=io.open("data1.txt","r")
    print(file:read())
    print(file:read())
    file:close()
    
    file=io.open("data1.txt","a")
    file:write("www.runoob.com5")
    file:close()
    

    垃圾回收

    Lua 提供以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:

    collectgarbage(“collect”), 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
    collectgarbage(“count”),以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
    collectgarbage(“restart”),重启垃圾收集器的自动运行。
    collectgarbage(“setpause”),将 arg 设为收集器的 间歇率 (参见 §2.5)。 返回 间歇率 的前一个值。
    collectgarbage(“setstepmul”),返回 步进倍率 的前一个值。
    collectgarbage(“step”),单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
    collectgarbage(“stop”),停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。

    mytable = {"apple", "orange", "banana"}
    
    print(collectgarbage("count"))
    
    mytable = nil
    
    print(collectgarbage("count"))
    print(collectgarbage("collect"))
    print(collectgarbage("count"))
    
    

    实现面向对象

    --对于一个对象来说  属性 方法
    
    person ={ name="MoGu",age=99 }
    
    person.eat = function ()
    	print(person.name.."在吃饭")
    end
    
    function person.eat()
    	print(person.name.."在吃饭")
    end
    
    person.eat()
    
    

    通过 “·” 来调用方法的时候,self 不会自动赋值,我们必须通过第一个参数来传递当前的 table。

    person ={ name="MoGu",age=99 }
    
    person.eat = function (self)
    	print(self.name.."在吃饭")
    end
    
    person.eat(person)
    
    a = person
    
    a.eat(a)
    
    

    通过 " : " 调用的时候,系统会自动传递当前的 table 给 self。

    person ={ name="MoGu",age=99 }
    
    function person:eat()
    	print(self.name.."在吃饭")
    end
    
    person:eat()
    
    a = person
    
    a:eat()
    
    

    new 新对象

    Person ={ name="MoGu",age=99 }
    
    function Person:eat()
    	print(self.name.."在吃饭")
    	print(self.name.."的年龄是"..self.age)
    end
    
    function Person:new(o)
    	local t = o or {}
    	--调用一个属性的时候,如果 t 中不存在,那么会在 __index 所指定的 table 中查找 setmetatable( t, { __index=self })
    	setmetatable(t,self)
    	self.__index=self
    
    	return t
    end
    
    Student = Person:new()
    Student.grade=1
    
    stu1 = Student:new()
    
    stu1:eat()
    print(stu1.grade)
    
    展开全文
  • 我们能用 lua 做什么

    万次阅读 2018-03-30 10:16:45
    女主宣言lua 是一个巴西人设计的小巧的脚本语言,它的设计目的是为了能够嵌入到应用程序中,从而为应用程序提供灵活的扩展和定制功能。今天我们邀请到 360 高级开发工程师李钢带我们快速入门 lue。本文最先发布于 ...
        

    640?wx_fmt=gif&wxfrom=5&wx_lazy=1

    女主宣言

    lua 是一个巴西人设计的小巧的脚本语言,它的设计目的是为了能够嵌入到应用程序中,从而为应用程序提供灵活的扩展和定制功能。今天我们邀请到 360 高级开发工程师李钢带我们快速入门 lue。本文最先发布于 opsdev,转载已获取作者授权。 

    PS:丰富的一线技术、多元化的表现形式,尽在“HULK一线技术杂谈”,点关注哦!

    前言

    作为web开发工程师,我们平时主要使用的开发语言是php。这个语言提供了对html模版的强大的处理能力,也提供了十分丰富的函数库及扩展,非常的适合web开发使用。那么lua是如何进入到我们的视线中的呢?在这里我先说下我在开发一个web产品时,会优先考虑的几个问题:


    1. 如何保证服务的稳定性,即如何防止白屏、50x错误的发生。

    2. 如何提高页面的响应速度,即让用户感觉页面打开足够快。

    3. 那么在用php解决这几个问题时,是否够用呢?


    答案在我看来是否定的,为什么这么说呢?请听我分别道来:


    1

    php在处理服务稳定性时的不足

    先简单说下由php导致的服务异常原因:


    1. php语法错误、运行时的异常都会导致500错误,这在用户端浏览器就会显示出白屏。

    2. 在使用nginx + php-cgi这种组合提供动态web服务时,当后台cgi进程挂掉或数量不够用时,即会产生502错误;当php执行时遭遇阻塞(如连db时,db压力过大),而在nginx中配置的超时时间到达后,通常会产生504错误。


    这几种异常本身都是由于php导致的,当然不能靠php去解决。


    webserver如nginx提供了如error_page这种用于处理当服务产生异常时的后续处理机制,为了不让用户看到白屏或错误页面,我们可以定时对正常服务时的页面做一个快照,当遇到服务异常时,就给用户这个历史快照看。


    但这样做也有个局限性:当你提供服务的页面很多时,又或是需要根据请求的参数做一些逻辑上的处理时,显然就很难做到了。


    你也许会说,我可以写nginx配置,让它分析请求参数,再做相应的逻辑处理。但是这样做的话,想想看你的nginx配置会有多么的复杂,多么的难以维护,而且就算你这样做,也不能解决所有的问题。比如说我有个接口要输出json字符串,或是其它别的格式,你总不能说我再去写个nginx扩展让它支持json吧。


    所以,在提高服务的稳定性方面,我们的需求是:


    1. 能用到webserver提供的错误处理机制。

    2. 能方便的处理请求参数,做需要的逻辑处理。


    2

    php在提高用户响应速度方面的不足

    php是一个阻塞式顺序执行的脚本语言,虽然支持多进程执行,但这种模式并不适合使用在并发量很高的web服务中。


    想像如果一个请求的处理过程中,你需要调用到多处外部资源或服务(db、rest接口),那么你的处理速度就要依赖于这些外部服务,而且是一个一个顺序处理的,它们越多,处理就越慢。


    php的multi_curl可以用来并发请求这些外部的rest服务,但这样做的话,依旧需要等全部的请求都处理完成,才能返回给用户。换句话说,如果某个外部服务很慢,那么用户看到页面打开依旧会很慢。


    也可以选择把页面分块,让慢的部分用js异步请求加载。但这样做的话,会增加服务器的访问量,每增加一块,访问量会增大一倍。


    所以,在提高页面的响应速度方面,我们的需求是:


    1. 耗时慢的服务能够做到异步加载,服务端每完成一部分的计算,就让页面展示这部分。

    2. 不能过大的增加服务器的压力。


    3

    nginx-lua模块

    最终,我们找到了nginx-lua?模块。这个模块会在每个nginx的worker_process中启动一个lua解释器,在nginx处理http请求的11个阶段中,你可以在其中的多个阶段用lua代码处理请求。


    这二者的结合,给我们的web开发带来了新的思路。下面我就来说下导航目前是如何使用它来解决问题的。


    4

    解决服务稳定性

    这里的思路很简单,我们会在error_page指令被执行后,用lua代码来接受参数,处理逻辑部分,最终会返回前端和用php处理看起来一致的内容。部分代码如下:


    nginx_conf:

    640?wx_fmt=jpeg

    这里大家看到,当请求出现50x错误时,会跳到location jump_to_error_page_api中,在这里面,content_by_lua_file指令会在content处理阶段启动指定好的lua脚本(这里是error_page_api.lua)来处理请求。我们再看下lua脚本中都做了什么:


    lua example:

    640?wx_fmt=jpeg

    这里大家可以看到,我们可以在lua脚本中接受请求参数,做和php一样的逻辑,最终输出前端需要的正确的内容。


    目前这套机制我们已经用在我们这边的一个重要用户页面上,目前都没有收到用户反馈说页面打不开,出现错误页这种,效果很是明显。


    5

    提高用户页面的响应速度

    上面提到的解决响应速度的几个需求,我们的思路是引入bigpipe的处理机制。关于bigpipe本文不做讲解,大家可以自行google。这个项目目前尚处于实验阶段,但我们已经实现了一个简单的demo:


    nginx_conf:

    640?wx_fmt=jpeg

    这里指定请求index.php会用bigpipe_index.lua处理。


    lua example:

    640?wx_fmt=jpeg

    这里在处理请求时,大致逻辑如下:


    1. 首先会先吐出首屏html部分及部分html框架代码。

    2. 接下来会启动3个lua协程,在nginx-lua这个模块的调度下以异步非阻塞的模式并发的来处理3个外部请求。

    3. 这3个外部请求各自的延时不同,但是任何一部分处理完成,都会直接返回给前端用于展示。


    通过这种方式,用户的页面响应速度得到了明显的提高,体验更好。


    6

    结束语

    正如lua官方给出的定义所说,lua很小巧,非常的适合嵌入已有的应用程序中,从而补足现有系统的一些缺憾,并扩展出新的功能。对nginx-lua模块的使用,笔者也还在研究中,但我相信更好的使用它,能为我们现有的web开发打开一扇新的窗户,理解更深层次的知识。

    参考资料:

        

    [1] https://www.lua.org/

    [2] https://github.com/openresty/lua-nginx-module

    HULK一线技术杂谈

    由360云平台团队打造的技术分享公众号,内容涉及云计算数据库大数据监控泛前端自动化测试等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享

    640?wx_fmt=gif
    展开全文
  • lua 中文版

    2020-07-30 23:31:55
    lua中文开发,清晰版,lua与nginx,lua中文开发,清晰版,
  • lua入门

    千次阅读 2019-03-08 17:19:24
    redis2.6以后可以在redis 中使用lua语言。 1、用lua的好处: 1)一个脚本包含多个操作,减少访问次数从而减少网络开销 2)原子操作 redis 对lua脚本是原子化执行方案 3)复用性 复用 lua脚本的逻辑 2、lua脚本...

    redis2.6以后可以在redis 中使用lua语言。
    1、用lua的好处:
    1)一个脚本包含多个操作,减少访问次数从而减少网络开销
    2)原子操作 redis 对lua脚本是原子化执行方案
    3)复用性 复用 lua脚本的逻辑
    2、lua脚本安装:
    1)下载安装包 并解压

     tar-zvxf lua-5.3.5.tar.gz
    

    2)执行 make linux 编译
    编译过程报错 lua.c:82:31: 致命错误:readline/readline.h:没有那个文件或目录
    百度一下得知,需要安装依赖
    执行 如下命令安装依赖:

    yum install libtermcap-devel ncurses-devel libevent-devel readline-devel
    
    

    重新解压编译

    [root@192 lua-5.3.5]# make linux test
    cd src && make linux
    make[1]: 进入目录“/usr/apps/lua/lua-5.3.5/src”
    make all SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl -lreadline"
    make[2]: 进入目录“/usr/apps/lua/lua-5.3.5/src”
    gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_2 -DLUA_USE_LINUX    -c -o lapi.o lapi.c
    gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_2 -DLUA_USE_LINUX    -c -o lcode.o lcode.c
    

    然后 运行 lua 就启动进入了 lua 如下:

    [root@192 ~]# lua
    Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
    > 
    

    3、初步使用
    lua特点 动态类型的语言,和js类似
    变量 分为 全局变量 和局部变量

    单行注释 –
    多行注释 --[[ ]]

    全局变量 a=1
    局部变量 local b=1
    逻辑表达式 ±*/
    测试代码如下 编辑脚本 luatest :
    vi testlua 输入内容 (lua testlua 执行脚本)

    a=1
    local b=2
    print(a+b)
    

    执行 lua luatest 结果
    在这里插入图片描述

    关系运算符
    a==b 比较两个值是否相等
    ~= 比较两个值是否不等

    a=1
    local b=2
    --print(a+b)
    print (a==b)
    print (a~=b)
    print(a=='1')
    

    执行 lua luatest 结果
    在这里插入图片描述

    逻辑运算符
    and
    or
    not

    测试脚本:

    true
    false
    [root@192 lua-5.3.5]# vi luatest
    print((a==b)and(a==1
    a=1
    local b=2
    --[[print(a+b)
    print (a==b)
    print (a~=b)
    print(a=='1')
    ]]
    print((a==b)and(a==1) )
    print((a==b)or(a==1) )
    print(not(a==b))
    

    执行 lua luatest 结果
    在这里插入图片描述

    字符串 操作
    … 拼接 a…b 拼接字符串 a和b 同 java 的a+b
    #计算字符串的长度 #a 字符串a 的长度
    测试代码

    str1='hello'
    str2='world'
    print(str1..str2)
    print(#str1)
    

    在这里插入图片描述

    条件判断 如下
    a=1
    if( a == 1) then
    print(‘a=1’)
    elseif( b == 2) then
    print(‘b=2’)
    else
    print(‘111’)
    end

    执行结果:
    a=1

    循环
    a=1
    while(a<10) do
    print(a)
    a=a+1
    end
    测试结果:
    在这里插入图片描述

    for i=1,5 do
    print(i)
    end
    测试
    在这里插入图片描述

    遍历数组

    local arr={‘a’,‘b’,‘c’}
    for i,v in ipairs(arr) do
    print(i…’=’…v)
    end
    [root@192 lua-5.3.5]# lua luatest
    1=a
    2=b
    3=c
    [root@192 lua-5.3.5]#

    测试结果如下

    local function add(a,b)
    return a+b;
    end
    print(add(1,2))
    "luatest" 46L, 474C written
    [root@192 lua-5.3.5]# lua luatest 
    3
    

    其他内置操作:Sting 操作字符串 Table 操作数组

    4、redis 整合 lua

    lua 提供了redis 的操作 如:redis.call(‘set‘,‘name’,‘zhang’)
    但是直接运行该段脚本提示错误 如下

    > redis.call('set','name','zhang');
    stdin:1: attempt to index global 'redis' (a nil value)
    stack traceback:
            stdin:1: in main chunk 
            [C]:
    

    原因是直接在lua环境中运行该脚本,没有依赖的redis引擎。所以报错。
    因为redis中提供了这个引擎,所以在redis中运行该段脚本就没有这个问题了。
    在redis-cli 中运行该段脚本的方式: eval “脚本” keynumbers key… arg…[参数(没有参数写0)]
    如下:

    127.0.0.1:6379> eval "redis.call('set','name','zhang')" 0
    (nil)
    127.0.0.1:6379> get name
    "zhang"
    127.0.0.1:6379> 
    

    有参数的测试:
    通过 1(传入几个键) ‘name’ ‘lisi’ 出入参数
    在脚本中 通过KEYS[1] 和 ARGV[1] 获取使用参数如下(下标从1开始):

    127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 'name' 'lisi'
    OK
    127.0.0.1:6379> get name
    "lisi"
    

    5、lua实现访问ip频率限制
    编写脚本

    local num = redis.call('incr',KEYS[1])
    if (tonumber(num)==1) then
        redis.call('expire',KEYS[1],ARGV[1])
        return 1
    elseif (tonumber(num)>tonumber(ARGV[2])) then
        return 0
    else
        return 1
    end
    

    然后执行脚本 10秒内 第11次返回为0 10秒后返回为1

    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10 , 10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 1
    [root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
    (integer) 0
    
    

    遇到的问题:
    执行lua脚本时 要 ./redis-cli --eval “iplimitlua.lua” ip1 , 10 10 这样来执行 (ip1 为ip地址key ,10 10 表示 10 秒内访问不能大于10次 并且逗号前后要有空格)
    而不能 先执行 ./redis-cli
    然后执行 -eval “iplimitlua.lua” ip1 , 10 10 这样会报错

    6、lua脚本原子性验证
    一个redis-cli 中执行一个死循环 如下:

    [root@192 bin]# ./redis-cli
    127.0.0.1:6379> eval "while true do print(1) end" 0
    

    另一个客户端也链接 redis 执行redis操作 提示 redis is buzy .不能执行任何操作。

    [root@192 bin]# ./redis-cli
    127.0.0.1:6379> get name
    (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
    

    只有杀死redis服务重启后才能正常访问redis

    7、jedis 执行lua脚本
    两种方式 一种是直接执行脚本
    一种是 将脚本缓存在服务端,以摘要的方式执行,避免每次都传递完整脚本到服务端。具体脚本如下:

    
    
    public class TestLUA {
    	public static String lua="local num = redis.call('incr',KEYS[1]) "+
    							"if (tonumber(num)==1) then "+
    							"    redis.call('expire',KEYS[1],ARGV[1]) "+
    							"    return 1 "+
    							"elseif (tonumber(num)>tonumber(ARGV[2])) then "+
    							"    return 0 "+
    							"else "+
    							"    return 1 "+ 
    							"end";
    	public static String sha =null;
    	public static String loadScript() throws Exception{
    		//
    		Jedis jedis=RedisManager.getJedis();
    		//将脚本缓存到服务端并返回摘要信息
    		String sha = jedis.scriptLoad(lua);
    		System.out.println("sha===>"+sha);
    		return sha;
    	}
    	
    	public static Object testLua1() throws Exception{
    		if(null==sha){
    			System.out.println("加载脚本");
    			sha=loadScript();
    		}
    		
    		Jedis jedis=RedisManager.getJedis();
    		//String sha = jedis.scriptLoad(lua);
    		List<String> keys = new ArrayList<String>();
    		keys.add("ip:limit:127.0.0.1");
    		
    		List<String> args = new ArrayList<String>();
    		args.add("30");
    		args.add("10");
    		//以摘要方式执行脚本
    		return jedis.eval(sha, keys, args); 
    		
    	}
    	
    	public static Object testLua2() throws Exception{
    		
    		Jedis jedis=RedisManager.getJedis();
    		List<String> keys = new ArrayList<String>();
    		keys.add("ip:limit:127.0.0.1");
    		
    		List<String> args = new ArrayList<String>();
    		args.add("30");
    		args.add("10");
    		
    		return jedis.eval(lua, keys, args); //这样每次都传递脚本到服务端增加网络消耗
    		
    	}
    	
    	public static void main(String[] args) throws Exception {
    		for(int i=0;i<13;i++){
    			System.out.println(testLua1());
    			//System.out.println(testLua2());
    
    		}
    		
    	}
    
    }
    
    
    展开全文
  • Lua游戏逆向及破解方法介绍

    千次阅读 2018-08-22 12:42:18
    随着手游的发展,越来越多的Cocos-lua端游开发者转移到手游平台。Lua脚本编写逻辑的手游也是越来越多,如梦幻西游、刀塔传奇、开心消消乐、游龙英雄、奇迹暖暖、疾风猎人、万万没想到等手游。随着Lua手游的增加,其...

    背景介绍

    随着手游的发展,越来越多的Cocos-lua端游开发者转移到手游平台。Lua脚本编写逻辑的手游也是越来越多,如梦幻西游、刀塔传奇、开心消消乐、游龙英雄、奇迹暖暖、疾风猎人、万万没想到等手游。随着Lua手游的增加,其安全性更值得关注,在此归纳一些常用的分析方法,同时介绍一些辅助工具。

    识别Lua游戏

    Android平台的apk包可以直接解压,找到./lib目录下的so逻辑模块,一个个分析其so,寻找是否内嵌lua引擎(一般情况下,最大的so最有可能内嵌lua引擎)。如果有libcocos2dlua、libhellolua字样,其内嵌lua引擎的可能性极大。

    将可疑so拖入IDA,查看lua引擎字符串,找到如图1所示的lua引擎内字符串,那该手游基本就可以确定是内嵌了一个lua引擎,有极大可能是用lua编写游戏逻辑。

     

    2222.jpg

    图1. Lua引擎相关字符串 

    也可以配合留意下解压出来的assets目录下,是否包含脚本信息。这类信息一般是加密的(也有很多安全意识薄弱的是直接lua脚本明文存放的),但有个明显特征是:有多个文件存放。如图2和图3所示,分别是两款非常火热的Lua手游的assets目录下的lua脚本信息。其中D手游仅是对luac进行加密,而M手游则是连名字也弄个哈希加密。

     

     

     

    图2. Lua手游asserts下可疑脚本信息案例手游D

     

    图3. Lua手游asserts下可疑脚本信息案例手游M

    破解思路

    Lua手游的破解主要分成两步,一步是能获取游戏lua脚本;第二步是替换lua脚本。核心是找到lua脚本,然后修改生效。不同安全级别的手游,相应的lua脚本获取时机点会有所不同。(本质是沿着Lua引擎加载lua脚本的整条加载链,如图4所示,去不断分析找到合适时机点dump和替换。)

    另一类思路,是辅助工具常用的方法,比如叉叉的lua手游辅助。只需要获取游戏lua脚本信息,然后无需替换,而是直接加载自身的一个lua脚本(该脚本和游戏脚本在同一个命名空间,可直接修改游戏脚本数据,调用函数)。

    一、 直接assets资源可获取lua脚本

    这类比较初级,在assets目录下可获得lua或者luac源码。

    针对lua源码类型,直接修改然后apktool重打包即可。

    针对luac源码类型(luac是lua编译的lua字节码文件,包含lua脚本所有信息,具体可搜索lua字节码文件结构),可以使用unluac等开源项目/工具反编译回lua源码。然后修改直接替换回修改后的lua源码文件即可(lua引擎加载脚本的时候,会识别luac magic number判断是lua源码还是luac,直接替换源码下去是不会影响脚本加载执行的)。

    二、 在luaL_loadbuffer函数处获取

    luaL_loadbuffer是一个走得比较频繁的加载点。Cocos引擎的lua加载器为cocos2dx_lua_loader,如图4所示,最终都是调用luaL_loadbuffer函数来加载。一般厂商会在这层上面对lua脚本进行解密,既是在luaL_loadbuffer函数获取buff参数可得到解密后的lua脚本。修改逻辑后可以直接在这个点替换回去。

     

     

     

    图4. cocos2dx_lua_loader函数

    三、 更底层的reader函数处获取

    lua引擎加载lua脚本最底层是到lua_reader函数。该函数负责最底层的脚本buff遍历,因此在此处dump出来的lua脚本是最纯正的lua脚本,所有加密都已经被去除(修改lua opcode或者引擎逻辑除外)。

    不过这个点的获取不到足够的文件信息(文件名、buff index等),需要配合上层函数拼凑lua脚本。

    常用工具

    ida.jpg

    一、 IDA工具

    可以进行动态调试和静态分析的工具,能在合适的位置下断点,修改指定寄存器和编写IDC脚本配合分析(这里可用来dump luac文件),不多介绍。

    二、 ChunkSpy

    用于解析lua字节码文件结构,方便luac的学习与阅读。

    88.jpg

    三、 unluac

    Unluac是一个lua反编译器开源项目,可将luac文件反编译为lua代码。针对梦幻西游、刀塔传奇等修改了lua opcode的手游,在静态分析确认还原opcode后,可修改这个项目打包出个对应版本的反编译工具(直接修改OpcodeMap.java里的map,改成对应游戏修改后的Opcode即可)。

    常用逻辑修改方法

    (1)修改全局变量(全局配置之类的)

    (2)update函数的调用逻辑修改(不update或者update多次)

    (3)敌人类、主角类、武器类、技能的初始化过程,属性修改

    (4)一些过程处理函数的逻辑修改(如伤害计算、命中部位判定等)

    实战案例

    案例一:修改全局变量(开心消消乐暴分)

    在lua_reader函数可直接dump出游戏lua源码,在其/zoo/gamePlay/GamePlayConfig.lua文件中配置了游戏分数奖励等属性,修改如图5 所示为几个特效额外得分即可轻松暴分。

     

     

     

    图5. 开心消消乐分数配置表

    案例二:多次调用游戏响应函数(刀塔传奇快速战斗)

    这里给出叉叉的一个例子。叉叉针对刀塔传奇有相应的辅助,其中快速战斗实现如图6 所示:是通过HOOK了update函数(lua中函数名是指针,备份原先函数指针,直接重写即可实现HOOK),在update函数尾调用10次tick实现。

    图6. 叉叉实现快速战斗

    案例三:修改函数内部逻辑(开心消消乐增加步数)

    在lua_reader函数可直接dump出游戏lua源码,如图7 所示:MoveMode::useMove函数负责处理步数扣除,直接修改为增加10步即可实现步数增加功能。

     

     

     

    图7. 开心消消乐步数修改

    小结

    Lua手游相对来说,还是不够安全的。因为lua引擎不仅是开源的,而且相对python等脚本引擎更简单。外挂作者在lua引擎底层如鱼得水。上面介绍的分析破解思路,就是沿着lua引擎加载脚本的函数链进行分析,找到一个解密后的点dump出脚本,针对脚本进行详细分析,尝试构造攻击点破解版游戏实现外挂功能。

    展开全文
  • XLua 源码学习原理(一)

    千次阅读 2019-09-29 10:28:27
    ...在XLua中可以直接使用Lua代码来调用C#中的代码。 CS.UnityEngine.Debug.Log('hello world') 接下来就是记录XLua是如何实现其调用的. XLua中的CS变量的功能 CS是一个全局的Table,所以C...
  • lua 中 . 和 : 的区别

    万次阅读 2019-01-05 16:27:27
    lua 中 . 和 : 的区别 首先在lua中使用“:”定义的函数会自动传入一个名为self的变量,这个变量是隐含的,self同c++中的this一样,表示当前对象的指针:而“.”定义的函数中没有self。 function class:func2( ) ...
  • Lua IDE 选择

    万次阅读 2019-06-03 08:45:13
    在图像修复的研究中,发现许多的实现代码用的是Torch,自然也就是要用Lua了。实际上去看了看,发现Lua还是小众语言,连IDE都没个强力的。 首先是ZeroBrane Studio,该项目在Github也有一千多个星。Linux上运行起来...
  • Windows下Lua安装环境配置教程

    千次阅读 2019-07-03 14:44:31
    首先是进入lua官网:https://www.lua.org/ 点击GetStart 点击LuaDist 进入下载界面 点击Windows86进行下载: 下载好之后,进行解压就行: 解压之后,在Windows环境变量Path添加解压文件的bin目录: ...
  • Lua】什么是 Lua 语言?

    千次阅读 2019-10-28 13:13:02
    一:Lua 语言简介。 二:Lua 语言的开发者及开发 Lua 的目的。 三:Lua 语言的两大特性。 四:Lua 语言的其它特性及应用场景。
  • 怎么用命令运行lua文本文件

    万次阅读 2017-10-28 23:23:17
    写好脚本script.lua,然后在控制台(cmd)下输入Lua script.lua, 需要Lua后边正确输入你的脚本所在的路径.
  • lua-5.1 和 lua-5.2 之间的一些差异。

    万次阅读 2012-07-04 07:36:29
    1. luaL_register 2 luaL_setfuncs lua5.1 之中有一个luaL_register,用于把luaL_Reg 数组中的所有函数注册到lua中。但在lua5.2中已经不支持这一函数了, lua5.2的手册中建议使用luaL_setfuncs来替代luaL_...
  • LUA require 搜索路径指定方法

    万次阅读 2012-12-13 09:48:28
    如果是一个 *.LUA 的文件, 里面用到了自己写的库, 或者第三方写的库, 但是你不想把它放到 lua 的安装目录里, 则在代码里面可以指定require搜索的路径。  package.path = '/usr/local/share/lua/5.1/?.lua;...
  • luac给lua脚本加密

    万次阅读 2018-08-22 13:13:01
    项目要求对lua脚本进行加密,查了一下相关的资料 ,得知lua本身可以使用luac将脚本编译为字节码(bytecode)从而实现加密,试了一下,确实可行。 下面是使用原生的lua解释器编译字节码: 1、新建一个名为1.lua的文件,...
  • lua 取整 取余

    万次阅读 2013-12-05 16:02:29
    使用math.modf(x/y)此函数,第一参数返回整数部分,第二个参数返回小数部分 t1,t2 = math.modf(30/10);
  • lua 5.2 实现luaL_register 功能

    万次阅读 2014-06-06 11:24:25
    lua 5.2 去掉了luaL_register功能,不能直接注册
  • lua调用不同lua文件中的函数

    万次阅读 2018-06-05 18:55:57
    a.lua和b.lua在同一个目录下 a.lua调用b.lua中的test方法,注意b中test的写法 _M 和 a中调用方法: b.lua local _M = {} function _M.test() ngx.say(&quot;hello test!&quot;) end return _M a.lua ...
  • Lua的require使用

    万次阅读 2015-10-11 20:03:54
    一.require的作用 1. 加载指定的模块,相当与#include作用类似,加载了该模块,那么就可已使用模块中的全局函数和全局数据(如表等等) 注:实际上require “xxx”后,会... require "showSome"(2)test.lua function C
  • Intellij Idea编辑调试Lua

    万次阅读 2018-06-04 20:37:52
    Intellij Idea编辑调试Lua 之前我整理了关于Intellij Idea以及EmmyLua插件调试Unity的相关资料 在学习Lua的过程中,想验证下学习过程中的猜想,最好支持断点调试这些, 所以去研究了一下Intellij Idea单独写Lua...
1 2 3 4 5 ... 20
收藏数 131,755
精华内容 52,702
关键字:

lua