關鍵字:
and break do else elseif end false for function if in local nil not or repeat return then true until while
使用變量不需要聲明,總是全局變量,除非加"local"。local的作用域是在最里層的end和其配對的關鍵字之間。全局變量的作用域是整個程序。大小寫相關。定義一個變量的方法就是賦值"="操作。變量類型,可以用type()函數來檢查:
Nil 空值,所有沒有使用過的變量都是nil。nil既是值又是類型。變量清除直接給變量賦以nil值。
Boolean 布爾值true 和 false。只有false和nil才被計算為false,而所有任何其它類型的值,都是true。比如0,空串等等,都是true。
Number 數值,相當于C語言的double實數如:4 0.4 4.57e-3 0.3e12 5e+20
Userdata 可以是宿主的任意數據類型,常用的有Struct和指針。
Thread 線程類型,在Lua中沒有真正的線程,將一個函數分成幾部份運行。
String 字符串,可以包含'/0'字符,可以定義很長的字符串。用雙引號或單引號引用單行,"[["和"]]"引用多行字符串。新版支持"[==["和"]==]"多層標記,=號個數為層數。嚴格按層數匹配。支持一些轉義字符:
/a鈴/b退格/f換頁/n換行/r回車/t制表符/v垂直制表//反斜杠/"雙引號/'單引號/[左中括號/]右中括號
Table 關系表類型,功能強大。可以用除了nil任意類型的值來作數組的索引和內容。
Table的定義:T1 = {} T1[1]=10 T1["John"]={Age=27, Gender="Male"}
這一句相當于T1["John"]["Gender"]="Male"
索引是字符串時可以簡寫成:T1.John.Gender="Male"或T1.John={Age=27, Gender="Male"}
第一,所有元素之間,總是用逗號","隔開;
第二,所有索引值都需要用"["和"]"括起來;如果是字符串,還可以去掉引號和中括號;
第三,如果不寫索引,則索引就會被認為是數字,并按順序自動從1往后編;
T1=
{
10, -- 相當于 [1] = 10
[100] = 40,
John= -- 如果你原意,你還可以寫成:["John"] =
{
Age=27, -- 如果你原意,你還可以寫成:["Age"] =27
Gender=Male -- 如果你原意,你還可以寫成:["Gender"] ="Male"
},
20 -- 相當于 [2] = 20
}
Function 函數也是一種類型,也就是說所有的函數本身就是一個變量。
函數的定義:function add(a,b) return a+b end 相當于add = function (a,b) return a+b end
如果函數只有一個參數,可以省略括號。
return語言一定要寫在end之前。在中間放上return,寫成:do return end。
函數可以接受可變參數個數,用"..."來定義function sum (a,b,...)
如 果想取得...所代表的參數,5.0版本可以在函數中訪問arg局部變量(表類型)得到。如 sum(1,2,3,4)則,a = 1, b = 2, arg = {3, 4},在5.1版本多了一個...變量(實際就是參數列表),區別只在于arg和...共同存在的情況,...會使arg變nil。
可以同時返回多個結果,比如:function s() return 1,2,3,4 end
a,b,c,d = s() -- 此時,a = 1, b = 2, c = 3, d = 4
使用面向對象編程:
t = { Age = 27, add = function(self, n) self.Age = self.Age+n end }
print(t.Age) -- 27
t.add(t, 10) 可以簡寫成:t:add(10)
print(t.Age) -- 37
單行注釋"--"。只要--后面第一個字符不是多行字符串引用符[[,即為多行注釋。
語句之間可以用分號";"隔開,也可以用空白隔開。
條件控制:if 條件 then … elseif 條件 then … else … end
While循環:while 條件 do … end
Repeat循環:repeat … until 條件
For循環:for 變量 = 初值,終點值,步進 do … end
可以省略步進值,這時候,for循環會使用1作為步進值。
For循環:for 變量1,變量2,… ,變量N in表或枚舉函數 do … end
語句塊用do 和 end 括起來的。可以在函數中和語句塊中定局部變量。
賦值語句可以同時給多個變量賦值。例如:a,b,c,d=1,2,3,4甚至是:a,b=b,a 方便的交換變量功能
默認變量總是全局的。定義局部變量,則在第一次賦值的時候用local說明。比如:
local a,b,c = 1,2,3 -- a,b,c都是局部變量
數值運算支持 +, -, *, /,^,#,%。^表示指數乘方。比如2^3 結果為8。5.1版加了#長度運算符。字符串的長度單位為字節,表的長度為nil前的整數索引個數,也就是數組的個數,如果有名為n的索引,它的值就是長度。5.1版引進%模運算。
用".."連接兩個字符串。如:"This a " .. "string." -- 等于 "this a string"
比較運算< > <= >= == ~=分別表示 小于,大于,不大于,不小于,相等,不相等。總是返回true或false。對于Table,Function和Userdata類型的數據,只有 == 和 ~=可以用。相等表示兩個變量引用的是同一個數據。比如: a={1,2} b=a print(a==b, a~=b) -- true, false
a={1,2} b={1,2} print(a==b, a~=b) -- false, true
邏輯運算and, or, not只有false和nil才計算為false,其它任何數據都計算為true,0也是true!
and 和 or的運算結果不是true和false,而是和它的兩個操作數相關。
a and b:如果a為false,則返回a;否則返回b
a or b:如果 a 為true,則返回a;否則返回b
運算符優先級,從高到低順序如下:
^
not - (一元運算)#
* / %
+ -
..(字符串連接)
< > <= >= ~= ==
and
or
=
常用標準庫函數:
print (···)把所有參數打印出來,利用tostring (e)轉換非字符。奇怪的是nil不能做..操作。
table.insert (table, [pos,] value)在pos位置插入一個值,默認是末尾。
table.remove (table [, pos])在pos位置刪除一個值,默認是末尾。
table.concat (table [, sep [, i [, j]]])用sep來連接數組里從i到j字符串或數字并返回一個長字符串,默認用空串從1到末尾。如果j大于i,返回空串。
table.maxn (table)返回最大正數索引或0。
table.sort (table [, comp])對數組排序,排序函數默認是<。
string.byte (s [, i [, j]])返回s串從i到j的數值。舊版只支持2個參數
string.char (···)和byte函數功能相反,返回0或多個數字對應的字符串。
string.find (s, pattern [, init [, plain]])從s串中從init位置開始找到第一個匹配模式的子串位置,并返回起點和終點。plain如果為true,忽略模式。如果使用了捕獲,則增加返回捕獲的部分。
string.gmatch (s, pattern)這是一個迭代函數,每次返回一個匹配的串。舊版叫gfind。如果使用了捕獲,則返回捕獲的部分。
string.gsub (s, pattern, repl [, n])用rep1替換。n限制替換個數。%1-9表示被捕獲的值。如果第3個參數是個函數并返回假,替換字符將保持原配而不是舊版的空串。
string.sub (s, i [, j])返回s從位置i到j的子串。負值表示從末尾往前數的位置。默認從1到末尾。
string.match (s, pattern [, init])從init匹配或捕獲值返回。舊版沒有這個函數。
string.format (formatstring, ···)格式化輸出。%q可以輸出安全字符串。
string.rep (s, n)把s復制n份
string.reverse (s)把s倒轉。舊版沒有這個函數。
string.len (s)計算字符串長度。
string.lower (s)小寫化。string.upper (s)大寫化。
io.read (···)"*n"讀一個數字,支持各種格式;"*a"讀整個文件;"*l":讀一行,默認;"n"讀n個字符
io.write (···)寫字符或者數字
io.lines ([filename])迭代返回文件中的一行。
pairs (t)迭代返回表中的鍵值對
ipairs (t)迭代返回數組中的索引和值
module (name [, ···])創建一個模塊。require (modname)加載一個模塊
模式表:
.任意字符%w字母和數字%a字母%d數字%p標點字符%s空白符%大寫字母表示相應集合的補集
%u大寫字母%l小寫字母%x十六進制數字%z代表0的字符%c控制字符
%是轉義標識 []集合 ^補集
()表示捕獲 %1-9是捕獲值 %bxy是以xy為標識的對稱結構
?匹配前一字符0次或1次 +匹配前一字符1次或多次
*最長匹配前一字符0次或多次 -最短匹配前一字符0次或多次
以^開頭的模式只匹配目標串的開始部分,相似的,以$結尾的模式只匹配目標串的結尾部分。
c調用lua函數一般的過程是:
1. load一個lua的文件,形成一個閉包并執行它
2. 在棧中壓入函數的名稱
3. 依次在棧中壓入函數的實參
4. 使用lua_pcall調用lua函數。 形式是: lua_pcall(L, 參數個數,結果個數, 錯誤處理函數在棧中的索引)
此時此函數相關的棧已經被清空。執行lua完畢后,如果成功,結果依次將壓入棧中, 如果不成功,錯誤信息將 壓入棧中
5. 從棧中讀取返回結果或者錯誤信息。
lua調用c函數(寫成庫的例子)
lua調用庫在windows下是dll文件,在unix/linux下面是so文件
vs的過程如下:
1. 新建一個dll的工程
2. 定義一個def文件,定義dll的導出,mylib.def定義如下:
LIBRARY mylib.dll
DESCRIPTION "first dll for lua"
EXPORTS
luaopen_mylib
3. 編寫庫,新建一個cpp文件.
4. 定義庫函數,這里以pil的lsin函數,輸出傳入參數的sin()值
5. 定義luaL_reg數組,這個是注冊一系列公開給lua調用的函數數組. 數組最后一個元素必須是 {NULL, NULL} 的luaL_reg結構用來做結束標識.
6. 用luaL_openlib聲明主函數
5.1版本和5.0版本的區別:
新模塊系統,增量垃圾收集,varargs新機制,多行字符串或引用的新語法,#和%新操作符, metatable支持所有類型,使用luaconf.h來配置lua暫時避免版本沖突,完善的reentrant parser。Pil第二版包括了5.1的新內容,增加了新例子,對新模塊系統,多狀態和垃圾收集的詳細闡述。
語法:
函數傳遞可變參數用...來代替局部arg表。不使用...時,arg用法和舊版本一樣;但使用...后(無論先后),局部arg都會變成nil。
在repeat.until里,局部變量的生命周期覆蓋到until條件后面;
多行字符串或引用的新語法使用多層匹配代替以前的嵌套;
#和%新操作符。
庫函數:
string.gfind改為string.gmatch;
如果調用string.gsub的第3個參數是個函數,如果函數返回假,替換字符將保持原配而不是舊版的空串;
table.setn廢棄,table.getn改為使用#;
loadlib改為package.loadlib;
math.mod改為math.fmod;
table.foreach和table.foreachi作廢。可用for循環pairs或ipairs代替;
require從package.path而不是LUA_PATH得到path值;
collectgarbage (opt [, arg])參數從[limit]改為更多選擇,gcinfo廢棄改為collectgarbage("count");
string.byte (s [, i [, j]])返回s串從i到j的數值。舊版只支持2個參數
string.match (s, pattern [, init])從init匹配或捕獲值返回。舊版沒有這個函數。
string.reverse (s)把s倒轉。舊版沒有這個函數。
module (name [, ···])創建一個模塊。舊版沒有這個函數。
C API:
5.1版本增加了以luaL_開頭的輔助庫Auxiliary Library;
luaL_getn改為lua_objlen,luaL_setn廢棄;
luaL_openlib改為luaL_register;
luaL_checkudata改為拋出異常而不是返回NULL。
luaopen_* functions不能直接調用,改為像調用其它普通c函數一樣的過程;
lua_open改為lua_newstate,可以設置內存分配方法。luaL_newstate默認使用realloc分配方法;
5.0的調用方法:
lua_State *L = lua_open();
luaopen_base(L);
luaopen_string(L);
luaopen_math(L);
5.1的調用方法:
lua_State *L = luaL_newstate();
lua_cpcall(L, luaopen_base, 0);
lua_cpcall(L, luaopen_io, 0);
lua_cpcall(L, luaopen_math, 0);
lua_cpcall(L, luaopen_string, 0);
lua_pushcfunction(L, luaopen_*);lua_call();等價于lua_cpcall(L, luaopen_*, 0);
其他經驗:
lua的擴展庫luasocket, luasql, luacom, kepler...
程序可以存成cpp也可以存成c, 如果以.c為擴展名就不需要加extern "C"
c和lua交互的時候,棧的編號是從1-n,也可以取負值,-1表示末尾
設置環境變量LUA_PATH="E:/LuaEdit/myproject/?.lua",然后使用require "lua包/文件名"。然后就可以直接使用lua文件里的函數而不需要dofile(路徑)了。在windows下的路徑如果是/必須寫成//,或者/。
lua5.1win32的解析器下載來后可以直接使用。luasocketwin32擴展包下載后,需要正確設置LUA_PATH=< LDIR>/?.lua;?.lua和LUA_CPATH=<CDIR>/?.dll;?.dll。然后運行lua解析器, require相應的socket包就可以了。
在VC++6下編譯帶lua的.dll文件的時候,在工程設置里必須指明LIBC.lib Libcmtd.lib的加載順序。選擇VC菜單Project->Settings->Link->Catagory然后在 Object/library Modules的Edit欄中填入LIBC.lib Libcmtd.lib。否則會出現:
Linking...
LIBC.lib(crt0dat.obj) : error LNK2005: __cinit already defined in LIBCMTD.lib(crt0dat.obj)
新版本下表迭帶需要加pairs(t),舊版本以下代碼結果是正確的:
Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio
> function print_contents(t)
>> for k,v in t do
>> print(k .. "=" .. v)
>> end
>> end
> print_contents{x=10, y=20}
stdin:2: attempt to call a table value
stack traceback:
stdin:2: in function 'print_contents'
stdin:1: in main chunk
[C]: ?
新版本中的...和arg不能同時出現,無論先后,...的出現都會使arg為nil:
print(#arg)
function test(...)
--print(...)
print(type(arg),#arg)
--print(...)
end
test(1,2,3,4)
print(#arg)
運行結果:
E:/lua5_1_2_Win32_bin>lua5.1 e....lua 1 2 3
3
table 4
3
結果說明arg在新版本中還是可以使用的,但跟...沖突,跟全局表arg不沖突。但本地arg會覆蓋全局的arg,如果兩者都要調用該如何處理?使用...后,編譯器仍然把函數里的arg看成是局部變量。因此變通的方法是:
print(#arg)
t=arg
function test(...)
print(...)
arg=t
print(type(arg),#arg)
--print(...)
end
test(1,2,3,4)
print(#arg)
這和舊版本是一樣的。看來引入...的用意不是為了區分全局和局部的arg。那究竟是為了什么呢??
lua安裝:
Windows:
把etc目錄下的luavs.bat 拷到lua的解壓根目錄下直接運行, 生成的.h .dll .lib .exe文件都在src下。
Linux:
$make linux && make install 標準的linux安裝
在Windows XP SP2下使用Visual C++ 6. 編譯lua的過程分為3步:
1,建立靜態庫工程,編譯庫文件
Lua庫由標準庫和核心庫組成,我用的是5.1版本,所有代碼都放在src目錄中。把src目錄中除lua.c,luac.c文件外所有的*.c文件添加到項目中,設置一下輸出路徑,F7編譯就可以了。
2,建立Win32控制臺工程 編譯lua.exe Lua解釋器
把lua.c添加到工程中,link選項中包含進剛才編譯好的lib文件,F7編譯
3.再建立Win32控制臺工程, 編譯luac.exe Lua編譯器
同上步一樣,編譯成功后生成luac.exe, 最好把這兩個文件放在新建的bin文件下,在添加進系統的環境變量。
tolua++在cygwin下不用SCons編譯:
詳見
http://lua-users.org/wiki/CompilingToluappWithoutSconssrc/lib下:
gcc -shared -o tolua++.dll *.c /usr/local/lib/lua51.dll -I../../include -I/usr/local/include
gcc -c *.c -I../../include -I/usr/local/include
ar rcsv libtolua++.a *.o
src/bin下:
gcc tolua.c toluabind.c -I../../include -I/usr/local/include -L /usr/local/lib -llua -L../lib/ -ltolua++
然后把所得到的文件復制到系統相應目錄中。
使用tolua用.pkg生成.cpp的時候,在生成的.cpp最前面設置#define TOLUA_API extern "C" __declspec(dllexport)可以使.dll文件導出時不改變文件名。貌似還要加
#ifndef __cplusplus#define __cplusplus#endif
使用cygwin產生的.dll文件不能被lua使用。用同一源文件在vc下生成的.dll文件是可以被lua用的。
使用VC編譯tolua時,把除了tolua.c和xxx.defalt以外的.c和.h都包含進去,然后在tolua++.h里設置 #define TOLUA_API extern "C" __declspec,不斷得修改#include "tolua++.h"在各個文件中的位置,使得用dumpbin看到65個導出函數為止。貌似VC6版本太低不能成功設置預編譯處理選項,因此這樣麻煩 點。編譯成功后就有.lib和.dll文件供接下來的.dll工程使用。
extern "C" _declspec(dillexport)
extern "C" void DLL_EXPORT __stdcall
============
實際應用場景練習
============
一、利用Lua的Table實現類的多繼承:
1、假設基類為B1、B2
2、繼承的類為A
3、使用的時候類似于:
Instance1 = A:new()
Instance1:Method1(arg1, arg2)
這個不難,但是務必要掌握table的應用
答案:見OO.lua。在lua解析器下運行:dofile "OO.lua"
二、利用lua的擴展包luasocket實現http的一個應用:
1、構造http協議,訪問
www.qq.com,具體的訪問方式網上有例子。
2、在返回來的http包中,取出騰訊公司的客服電話號碼 -- 需要使用到lua的字符串查找和模式匹配函數。
答案:
下載lua5.1win32的解析器和luasocketwin32擴展包,正確設置LUA_PATH=< LDIR>/?.lua;?.lua和LUA_CPATH=<CDIR>/?.dll;?.dll,<CDIR>指向 luasocket-2.0.1-lua5.1-win32/lib,<LDIR>指向luasocket-2.0.1-lua5.1- win32/lua。lua程序見Phone.lua。在lua解析器下運行:dofile "Phone.lua"
----------------------------
http = require("socket.http")
qqweb = http.request("
http://www.qq.com")
tel=string.match(qqweb,"騰訊客服電話:(%d+%-%d+)")
print("您要的電話號碼是:"..tel)
-------------------------------
三、在Windows下,利用C++實現一個基本的Socket類庫來給Lua使用:
1、結合tolua++進行編譯,生成Lua可以調用的dll文件,請參考網上的資料來熟悉tolua++的使用
2、假設dll文件名為MySocket.dll,類為CMySocket,則調用的時候類似于:
require("MySocket")
Instance1 = CMySocket:new()
Instance2 = CMySocket:new()
Instance1:Listen(9701)
Instance2:Connect("127.0.0.1",9701)
答案:
windows下編譯lua:
把etc目錄下的luavs.bat 拷到lua的解壓根目錄下直接運行, 生成的.h .dll .lib .exe文件都在src下。
把相應的.h文件和lua51.lib文件拷貝到VC相應目錄下。lua.exe和lua51.dll為lua解析器。
在cygwin下編譯安裝方法:$make linux && make install
用cygwin編譯安裝tolua++,且不用SCons編譯:
src/lib下:
動態鏈接文件:
gcc -shared -o tolua++.dll *.c /usr/local/lib/lua51.dll -I../../include -I/usr/local/include
靜態連接文件:
gcc -c *.c -I../../include -I/usr/local/include
ar rcsv libtolua++.a *.o
src/bin下:
執行文件,改名為tolua++.exe:
gcc tolua.c toluabind.c -I../../include -I/usr/local/include -L /usr/local/lib -llua -L../lib/ -ltolua++
然后把所得到的文件復制到系統相應目錄中。
根據mysocket.h設計mysocket類模式,寫成.pkg文件。
產生binding文件mypkg.cpp備用,此時要用到mysocket.h:tolua++ -o mypkg.cpp mysocket.pkg
適當修改一下生成的mypkg.cpp文件,使以后導出的.dll文件函數名不變。
#define TOLUA_API extern "C" __declspec
用以下方法在cygwin下編譯的.dll文件不能被lua使用,加載包會失敗。
g++ -shared -o MySocket.dll *.cpp /usr/local/lib/lua51.dll -I/usr/local/include -I.. -L /usr/local/lib -ltolua++
改用VC來編譯。先編譯tolua++。
把src下除了tolua.c和toluabind_default.x以外的.c和.h都包 含進去。工程設置里要包含LIBC.lib Libcmtd.lib tolua.lib lua5.1.lib,include和lib路徑也要包含lua的。然后設置#define TOLUA_API extern "C" __declspec編譯成功后就有tolua.lib和tolua.dll文件供接下來的.dll工程使用。
把bingding文件mypkg.cpp和寫好的mysocket.cpp,mysocket.h一起在VC下編譯生成.dll文件。工程設 置里要包含LIBC.lib Libcmtd.lib tolua.lib lua51.lib,include和lib路徑也要包含lua或tolua的。前面兩個文件順序一定不要寫錯,否則產生如下錯誤:
Linking...
LIBC.lib(crt0dat.obj) : error LNK2005: __cinit already defined in LIBCMTD.lib(crt0dat.obj)
編譯產生的MySocket.dll文件就可以被lua調用了。
運行一個lua解析器程序實例,執行:
require("MySocket")
Instance1 = CMySocket:new()
Instance1:Listen(9701)
再運行另一個lua解析器程序實例,執行:
require("MySocket")
Instance2 = CMySocket:new()
Instance2:Connect("127.0.0.1",9701)