前言
竹子是 java 程序員一枚,最近在做一個登錄的改造,用 lua 實現,現在基本算是告一段落,然后在此分享下在過程中遇到的坑吧。
一定要注意使用 lua 的版本,版本不同,可能有的函數就沒有了,比如 bit 中的 math.mod 函數,5.1 之后就改為 fmod() 了,但是當時引入的還是之前的版本,就報錯了,報錯了,但是還找了好久的錯誤,也是醉了。這些可以參考 lua 官網的發布說明,看看每個版本的發布說明 。
http://www.lua.org/manual/5.1/manual.html#7.2
1.字符串拼接(不是 + 而是 "..")
做過java 的都知道,java 中字符串的拼接使用 + ,但是在 lua 里千萬要注意,不是"+", "+" 在 Lua 里只表示算術運算,真正的拼接字符串是 ".."
看實例:
local str = "Hello," + 'bamboo' ngx.say(str)
看運行結果:
2018/02/01 07:48:40 [error] 753#0: *766 lua entry thread aborted: runtime error: /app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua:182:
attempt to perform arithmetic on a string value (在 string 類型的數據上執行算術運算,所以肯定報錯了,心累) stack traceback: coroutine 0: /app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua: in function </app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua:1>,
client: 172.17.0.1, server: localhost, request: "GET /ng_test HTTP/1.1", host: "localhost:8008"
這個一定要注意,竹子就是因為不注意,出了多次錯,做為 java 程序員,這就是順手的事呀。哈哈....
2.方法的調用 "." 和 ":"
這個也是很容易出錯的地方,因為方法調用的時候用了".",而沒有用 ":",出了好多次問題,寶寶心里苦呀,有的時候用 ".",有的時候用 ":",寶寶容易暈呀,有沒有。不說了,來看例子.
ng_test.lua
local util1 = require 'util1' ngx.say(util1.tt2('bamboo', 'Beijing'))
util1.lua
local _M = {} function _M.tt1(name, address) return 'user:' .. name .. ',address:' .. address end function _M:tt2(name, address) return 'user:' .. name .. ',address:' .. address end return _M
然后運行 nginx,發現報下面的錯
2018/02/01 08:00:46 [error] 772#0: *772 lua entry thread aborted: runtime error: /app/lua_pro/lua_exercise/util/util1.lua:10:
attempt to concatenate local 'address' (a nil value) stack traceback: coroutine 0: /app/lua_pro/lua_exercise/util/util1.lua: in function 'tt1' /app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua:191: in function </app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua:1>,
client: 172.17.0.1, server: localhost, request: "GET /ng_test HTTP/1.1", host: "localhost:8008"
wtf, address is nil,kidding me? 那到底是怎么回事呢,其實如果細心的話,會發現有 tt1 和 tt2 兩個函數,然后 tt1 聲明的時候用的是 ".",tt2 聲明的時候用的是":"
>帶點號的函數聲明: ".",默認是沒有 self 參數的
>冒號聲明的函數,默認有一個隱藏的參數 self,so 在你調用的時候要用 ":",而不是 "."
因為正確的應該是 util1:tt2('Hello,', 'bamboo')
當然你調用 tt1 的時候直接 util1.tt1('Hello,', 'bamboo')即可。
關於 "." 和 ":" 的區別可以看看這里:https://www.lua.org/pil/16.html
tips:
我的經驗是,用冒號聲明,就用冒號調用;用點號聲明,就用點號調用.保持一致,一般情況下喜歡用冒號聲明,因為有一個 self 可以很方便的使用,上面給的鏈接中也涉及到了self 的用處。
3. nil 這個值是最令人頭疼的問題了
大家應該都知道,在代碼中經常涉及到字符串、變量的拼接,但是有的時候可能變量會為空,也就是 nil,然后我們拼接了 nil 值,程序立馬報錯。這就尷尬了,而且 lua 的異常處理機制跟 java 比起來還是要差了很多。先不說異常處理,我說先說 nil 處理吧
ng_test.lua
-- if you haven't assigned a default value to a variable,it is nil.After that,you concat it with a string value,so definitely, it will occur exception local str ngx.say("Hello" .. str)
exception code:
2018/02/01 08:44:43 [error] 782#0: *774 lua entry thread aborted: runtime error: /app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua:199:
attempt to concatenate local 'str' (a nil value) stack traceback: coroutine 0:/app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua: in function </app/lua_pro/lua_exercise/mobile_login_exercise/ng_test.lua:1>,
client: 172.17.0.1, server: localhost, request: "GET /ng_test HTTP/1.1", host: "localhost:8008"
一個變量,你沒有賦默認值,他就是 nil,所以一般情況下最好根據類型賦個默認值,
比如 table local tmp_tab = {} string local str = '' number local n = 0 boolean local flag = true and so on .....
當然有的時候我們通過函數返回一個值,我們可以做個三元表達式,避免 nil 異常.
local return_result = func() return_result = (return_result and {return_result} or {''})[1] return 'Hello,' .. return_result
所以,我們最好每個返回值做拼接的時候加上三元表達式的判斷
4. table 循環的時候遇到 nil 值會截斷,而且不按照他原始的順序輸出
來,直接看 demo 吧
local _tab = { log_param1 = 'hello,', log_param2 = 'nice to meet you.', log_param3 = nil, log_param4 = 'How are you?', } for k, v in pairs(_tab) do local tmp_v = v if not tmp_v then tmp_v = '' end ngx.say('k:' .. k .. ',v:' .. v .. '<br/>') end
--輸出,可以看到並不是 1 2 4 的輸出,而是 1 4 2,我要根據傳遞的參數來,拼接日志,日志--是要求順序的,這樣肯定是不行的。 --k:log_param1,v:hello, --k:log_param4,v:How are you? --k:log_param2,v:nice to meet you.
所以我們來嘗試按照正常順序獲取值,如果遇到 nil,我們就拼接空嘛,但是順序不對,那肯定是不行的了。(可能有的人說,那可以直接指定好變量名呢,不是更方便嘛,但是你要知道,這個是一個公共的方法,傳遞的參數個數是不確定,也不是確定的變量名,so 我們繼續我們的 solution)
--計算 tab 的長度 local i = 0 for k in pairs(_tab) do i = i + 1 end
--循環取值 local msg = '' for n = 1, i do local tmp_v = _tab['log_param' .. n] if not tmp_v then tmp_v = '' end msg = msg .. tmp_v end ngx.say(msg) --輸出,不對呀,按照我們的預想,應該是 Hello,nice to meet you.How are you?怎么不一--樣呢 hello,nice to meet you.
--其實取 _tab 長度 i 的時候就取錯了,取的是 3 而不是 4 ,所以了肯定不對。那我們要怎么做呢?
看下面:
local i = 0 for k in pairs(_tab) do k = tostring(k) local index = ngx.re.find(k, '([0-9]+)', 'jo') local tmp_k = string.sub(k, index, #k) tmp_k = tonumber(tmp_k) if tmp_k then if i < tmp_k then i = tmp_k end end end ngx.say('i is:' .. i .. '<br/>')
改成這樣之后就可以了,可能有的人又會問了,如果最后一個是 nil,那獲取到的最大值是 3 也不對呢,但是我們要的拼接字符串,為空我們就不拼接了嘛,對吧,也想當於是實現我們要的效果了嘛。哈哈。
當然還有很多,今天先寫到這里,以后有新的會補充進去。