lua 小技巧
-
把常用的工具函數添加到 _G 里面,所有的文件都可以直接調用:
-- 在 a 文件中將工具函數添加到 _G: _G.IsEmptyStr = function(str) return str==nil or type(str) ~= "string" or str == "" end _G.PrintObjPos = function(prefix, obj) prefix = prefix or "" local l,t,r,b = obj:GetObjPos() XLPrint(prefix .. " l=" .. l .. ", t=" .. t .. ", r=" .. r .. ", b=" .. b) end -- 在其它文件中直接使用工具函數: if not IsEmptyStr(obj:GetText()) then PrintObjPos("[Dongyu]", obj) end
-
使用
or
操作符賦默認值:num = num or 0
-
使用
(a and b) or c
操作符實現 C 語言中a ? b : c
的功能 :num = (num < 0 and 0) or num 這樣做的原理是: a and b -- 如果 a 為 false, 則返回 a, 否則返回 b a or b -- 如果 a 為 true, 則返回 a, 否則返回 b
-
獲取 UTF-8 字符串中的字符數(中英文混合) :
local function strlength(str) -- 計算字符串長度,中英文混合 str = string.gsub(str, "%%", " ") -- 將%替換成" " local str = string.gsub(str, "[\128-\191]","") local _,ChCount = string.gsub(str, "[\192-\255]","") local _,EnCount = string.gsub(str, "[^\128-\255]","") return ChCount + EnCount end
這種做法跟 UTF-8 格式有關,標准 ASC|| 碼(英文)是 0-127 ;中文占 3 個字符(192-255)(128-191)(128-191);
詳情可參考這篇文章 -
去除字符串首尾空格:
function trim (s) return (string.gsub(s, "^%s*(.-)%s*$", "%1")) end
-
關閉 string.find(s, pattern, start, plain) 的模式匹配:
-- 將 find 的第四個參數設定為 true, 則 pattern 將被視為普通字符串,不會處理特殊字符 pos = string.find(str, "%s", 1, true)
-
分割字符串:
function split(s, delim) if type(delim) ~= "string" or string.len(delim) <= 0 then return end local start = 1 local t = {} while true do local pos = string.find (s, delim, start, true) -- plain find if not pos then break end table.insert (t, string.sub (s, start, pos - 1)) start = pos + string.len (delim) end table.insert (t, string.sub (s, start)) return t end
-
table.concat 打印數組:
local t = {"2016", "3", "6"} print(table.concat(t, "-")) -- 2016-3-6
-
打印 table:
function print_lua_table (lua_table, indent) local function print_func(str) XLPrint("[Dongyu] " .. tostring(str)) end if lua_table == nil or type(lua_table) ~= "table" then print_func(tostring(lua_table)) return end indent = indent or 0 for k, v in pairs(lua_table) do if type(k) == "string" then k = string.format("%q", k) end local szSuffix = "" if type(v) == "table" then szSuffix = "{" end local szPrefix = string.rep(" ", indent) formatting = szPrefix.."["..k.."]".." = "..szSuffix if type(v) == "table" then print_func(formatting) print_lua_table(v, indent + 1) print_func(szPrefix.."},") else local szValue = "" if type(v) == "string" then szValue = string.format("%q", v) else szValue = tostring(v) end print_func(formatting..szValue..",") end end end
-
拷貝 table:
function copy_table(ori_tab) if type(ori_tab) ~= "table" then return end local new_tab = {} for k,v in pairs(ori_tab) do local vtype = type(v) if vtype == "table" then new_tab[k] = copy_table(v) else new_tab[k] = v end end return new_tab end
或
function deepcopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local new_table = {} lookup_table[object] = new_table for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end return setmetatable(new_table, getmetatable(object)) end return _copy(object) end
-
for 循環中 remove 數組元素:
local t = {1,2,3,3,5,3,6} for i,v in ipairs(t) do if v == 3 then table.remove(t,i) end end -- 錯誤,第四個 3 沒有被移除,ipairs 內部會維護一個變量記錄遍歷的位置, -- remove 掉第三個數字 3 之后,ipairs 下一個返回的值是 5 而不是 3 local t = {1,2,3,3,5,3,6} for i=1, #t do if t[i] == 3 then table.remove(t,i) i = i-1 end end -- 錯誤,i=i-1 這段代碼沒有用,i 的值始終是從 1 到 #t,for 循環里修改 i 的值不起作用 local t = {1,2,3,3,5,3,6} for i=#t, 1, -1 do if t[i] == 3 then table.remove(t,i) end end -- 正確,從后往前遍歷 local t = {1,2,3,3,5,3,6} local i = 1 while t[i] do if t[i] == 3 then table.remove(t,i) else i = i+1 end end -- 正確,自己控制 i 的值是否增加
-
table.sort(t, comp) 排序數組 :
sort 可以將 table 數組部分的元素進行排序,需要提供 comp(a,b) 函數,如果 a 應該排到 b 前面,則 comp 要返回 true 。注意: 對於 a==b 的情況,一定要返回 false:
local function comp(a,b) return a <= b end table.sort(t,comp) -- 錯誤,可能出現異常:attempt to compare number with nil local function comp(a,b) if a == nil or b == nil then return false end return a <= b end table.sort(t,comp) -- 錯誤,可能出現異常:invalid order function for sorting -- 也可能不報這個異常,但結果是錯誤的;
之所以 a==b 返回true 會引發這些問題,是因為 table.sort 在實現快速排序時沒有做邊界檢測:
for (;;) { while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) // 未檢測邊界, i 會一直增加 { if (i>=u) luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); } while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) // 未檢測邊界, j 會一直減少 { if (j<=l) luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); } if (j<i) { lua_pop(L, 3); break; } set2(L, i, j); }
看以上代碼,如果 a==b 時返回 true 且邊界上的幾個值是相等的話, sort_comp 就無法阻止 i 繼續增長,直到超出邊界引發異常 attempt to compare number with nil;
即使對 a 和 b 進行了非空判斷,也會因為 i 超過邊界而引發異常 invalid order function for sorting.