学习Lua碰到的问题、踩坑记录
文章目录
- 一、遍历字典是无序的
- 二、Lua的遍历和C#不同
- 三、同名传参和同名字段
- 四、传参是引用传递
- 五、rawget()和rawset()
- 六、在Windows上安装Lua
- 1.get a binary
- 2.选择自己电脑的版本
- 3.解压文件,(存放文件地址文件夹名全部不要出现中文最佳)
- 4.修改环境变量
- 5.win+R,cmd,输入lua或者lua53后如下即安装完成
- 6.最后intellij idea 中推荐一个EmmyLua,使用安装比较简单,在设置setting里面选择Plugins,查找EmmyLua安装即可
- 七、把数值转成16进制的格式
- 八、把16进制色号转成三元数Color
- 九、去掉商里的小数
- 十、string.format() 保留3位小数
- 十一、向上/下取整, 四舍五入
- 十二、string拆成表, (不支持中文)
- 十三、敏感词过滤, 替换成*
- 十四、通配符, 正则表达式
- 十五、用正则实现trim()
- 十六、字符串截取string.sub()
- 十七、数组增删
- 十八、if后面跟一个方法, 这个方法会运行
- 十九、匿名函数
- 二十、死循环
- 二十一、右移: “>>”, 按位与: “&”
- 二十二、左移数值越界
- 二十三、可变参数: ‘…’
- 二十四、浅拷贝表
- 二十五、字典用table.sort()排序
- 二十六、多条件排序
- 二十七、Lua里没有string.split()
- 二十八、lua特殊写法
- 二十九、变量名
- 三十、今天星期几
- 三十一、os.time(): 秒
- 三十二、裆下日期转成表, 再把表转成秒
- 三十三、看身份证满18没有
- 三十四、Lua没有"继承"的弊端
- 三十五、取字符串的长度
- 三十六、#号
- 三十七、身份证验证
- 三十八、逻辑运算符的优先级
- 三十九、虚变量
- 四十、匹配字母和数字
- 四十一、if not a
- 四十二、墙裂推荐一个VS Code的插件: Rainbow Fart
- 四十三、数组洗牌打乱
- 四十四、JavaBean的要求规范
- 四十五、实现Switch
- 四十六、实现continue
- 四十七、string.format
- 四十八、三元表达式
- 四十九、"逻辑与"的截断机制
- 五十、问题:Lua的输出乱码
- 五十一、问题:Lua不是内部或外部命令,也不是可运行的程序或批处理文件
- 五十二、判断table里有没有某元素
- 五十三、(不)连续表的遍历
- 五十四、正则表达式
- 五十五、取模和取余
- 五十六、#汉字长度
- 五十七、赋值多个值
- 五十八、交换2个值
- 五十九、返回多个值
- 六十、方法后面不加小括号
- 六十一、按位运算
- 六十二、// 运算, 取商
一、遍历字典是无序的
t = {}
t.a = '1'
t.b = '2'
t.c = '3'
t.d = '4'
for i,v in pairs(t) doprint(v)
end-- 每次输出都是乱序的!!!
二、Lua的遍历和C#不同
function Cnt()print('执行Cnt方法')return 3
end
for i = 1, Cnt() doprint(i)
end输出:
执行Cnt方法
1
2
3
三、同名传参和同名字段
同名传参, 第1个传参无效。
function Do(arg, arg)print(arg)
end
Do(1, 2)输出:
2
同名字段, 第2个字段无效。
a, a = 1, 2
print(a)输出:
1
四、传参是引用传递
表作为参数传递时, 是引用传递, 传的是地址。
在C#里也是这样的:
如下↓, 改变"参数表"(arg)的内容, “原来的表”(tab)也会改变。
function ReName(arg)arg.name = '咸蛋超人'
end
tab = {name = '奥特曼'}
ReName(tab)
print(tab.name)输出:
咸蛋超人
但是, 改变arg表自身, tab却不会变。
function ReName(arg)arg = {name = '咸蛋超人'}
end
tab = {name = '奥特曼'}
ReName(tab)
print(tab.name)输出:
奥特曼
改变attr.name, 会同时改变ultraman.attr.name。
置空attr.name, 会同时置空ultraman.attr.name。
如下↓:
ultraman = {attr = {name = '奥特曼'}}
attr = ultraman.attrattr.name = '咸蛋超人'
print(ultraman.attr.name)attr.name = nil
print(ultraman.attr.name)输出:
咸蛋超人
nil
改变attr, 不会同时改变ultraman.attr。
置空attr, 不会同时置空ultraman.attr。
如下↓:
ultraman = {attr = {name = '奥特曼'}}
attr = ultraman.attrattr = {name = '咸蛋超人'}
print(ultraman.attr.name)attr = nil
print(ultraman.attr.name)输出:
奥特曼
奥特曼
结论:
改变attr内部字段, ultraman.attr会同时改变。
但是改变attr自身, ultraman.attr不变。
在lua中, 如果我想置空attr怎么办呢? 只能在方法外把它置空。
在C#中, 可以给传参添加ref关键字, 这样一来, 置空attr, 原本的attr也会被置空。
五、rawget()和rawset()
只对自身get和set, 而不访问元表。
otab = {a = '我是元表a'}
tab = {a = '我是a'}
setmetatable(tab, {__index = otab, __newindex = otab})-- 获取tab的a, 与元表无瓜
print(rawget(tab, 'a'))
print(tab.a)
print(otab.a)-- 给tab的b赋值, 与元表无瓜
rawset(tab, 'b', '我是b')
print(tab.b)
print(otab.b)输出:
我是a
我是a
我是元表a我是b
nil
六、在Windows上安装Lua
Lua下载地址:Lua:download
1.get a binary
2.选择自己电脑的版本
3.解压文件,(存放文件地址文件夹名全部不要出现中文最佳)
4.修改环境变量
5.win+R,cmd,输入lua或者lua53后如下即安装完成
6.最后intellij idea 中推荐一个EmmyLua,使用安装比较简单,在设置setting里面选择Plugins,查找EmmyLua安装即可
七、把数值转成16进制的格式
print(string.format('%x', 1221197823))输出:
48c9ffff
八、把16进制色号转成三元数Color
比如色号是: FFA2FC, 先用string.sub()把她肢解为: 红FF, 绿A2, 蓝FC。
再用tonumber(str, 16)把16进制转为10进制, (例如: FF转为255)。
再用rgb分别除以255。
最后new一个三元数Color。
local colorString = 'FFA2FC'
local r = tonumber(string.sub(colorString, 1, 2), 16) / 255
local g = tonumber(string.sub(colorString, 3, 4), 16) / 255
local b = tonumber(string.sub(colorString, 5, 6), 16) / 255
return UnityEngine.Color(r, g, b)
九、去掉商里的小数
用"/"来做除法, 得到的结果中总会带有小数, 像这样:
print(700 / 100)
print(701 / 100)输出:
7.0
7.01
需要实现:
当结果是7.0时, 去掉小数部分, 结果为7。
当结果是7.01时, 保留小数部分, 结果为7.01。
思路: 用math.modf(), 把整数部分与小数部分拆开:
local t1, t2 = math.modf(700 / 100)
print(t1)
print(t2)输出:
7
0.0
再加个判断"小数部分是否为0"就好了:
function GetFixedQuotient(num1, num2)quotient = num1 / num2local t1, t2 = math.modf(quotient)-- 去掉数字后面的.0if t2 == 0 thenreturn t1elsereturn quotientend
endprint(GetFixedQuotient(700, 100))
print(GetFixedQuotient(701, 100))输出:
7
7.01
十、string.format() 保留3位小数
print(string.format('%.3f', 0.5))
print(string.format('%.3f', 0.0005))
print(string.format('%.3f', 0.0004))
print(string.format('%.3f', 500))输出:
0.500
0.001(这里四舍五入了)
0.000(这里四舍五入了)
500.000
十一、向上/下取整, 四舍五入
-- 取整
print('向上取整: 1 / 3 = ' .. math.ceil(1 / 3))
print('向下取整: 1 / 3 = ' .. math.floor(1 / 3))
-- 想实现四舍五入, 就+0.5
print('四舍五入: 1 / 3 = ' .. math.floor(1 / 3 + 0.5))
print('四舍五入: 2 / 3 = ' .. math.floor(2 / 3 + 0.5))
-- 保留3位小数的四舍五入, 就先*1000再+0.5再/1000
print('四舍五入: 1 / 3 = ' .. math.floor(1 / 3 * 1000 + 0.5) / 1000)
print('四舍五入: 2 / 3 = ' .. math.floor(2 / 3 * 1000 + 0.5) / 1000)输出:
向上取整: 1 / 3 = 1
向下取整: 1 / 3 = 0
四舍五入: 1 / 3 = 0
四舍五入: 2 / 3 = 1
四舍五入: 1 / 3 = 0.333
四舍五入: 2 / 3 = 0.667
十二、string拆成表, (不支持中文)
input = "2b*)我"
tabA = {}
for i = 1, #input dolet = string.sub(input, i, i)table.insert(tabA, i, let)print(i, let)
end输出:
1 2
2 b
3 *
4 )
5 �
6 �
7 �
十三、敏感词过滤, 替换成*
input = 'ABc大大大'
word = 'bc大'
lower = string.lower(input)
result = lower
if string.find(lower, tostring(word)) ~= nil then-- 汉字占3个字符, 所以不能直接用string.len()local _, count = string.gsub(word, '[^\128-\193]', '')local stars = string.rep('*', count)result = string.gsub(lower, tostring(word), stars)
end
print(result)输出: A***大大
十四、通配符, 正则表达式
s = "abc \"it's a cat\""
_, _, _, d = string.find(s, "([\"'])(.-)%1")
print(d) -----输出: it's a cat
十五、用正则实现trim()
name = ' 蒂纳 奥特曼 '
local _name = string.gsub(name, '^%s*(.-)%s*$', '%1')
print(_name)输出:蒂纳 奥特曼
十六、字符串截取string.sub()
s = 'abcde'print(string.sub(s, 1, 3)) -- 从1数到3
print(string.sub(s, 3, 3)) -- 从3数到3
print(string.sub(s, 3)) -- 从3数到末尾
print(string.sub(s, 8)) -- 没有8,直接空了
print(string.sub(s, 3, -1)) -- 从3数到5(末尾)
print(string.sub(s, 3, -2)) -- 从3数到4(末尾-1)
print(string.sub(s, -4, -2)) --从2(末尾-3)数到4(末尾-1)
print(string.sub(s, -9))-- 从头数到尾-- 输出:
abc
c
cdecde
cd
bcd
abcde
十七、数组增删
数组能用, 字典不行。
table.insert(list, [pos, ]value), -- 不给pos就是#list+1
table.remove(list[, pos]), -- 不给pos就是#list
注意: 在for的时候, 要从后往前删。
十八、if后面跟一个方法, 这个方法会运行
function fun()print(2)
end
if fun() then
end输出: 2
↓写成这样则不执行。
if fun then
end
十九、匿名函数
注意: 直接 a = fun(), fun会立即执行, 不想立即执行的话, 就用个匿名方法把它包起来, 或者像delegate的+=一样, 方法后面别带括号。
function foo(arg)print(arg)
end---------1----------
arg1 = 1
-- 直接执行, 输出1
fun = foo(arg1)---------2----------
arg2 = 2
-- 不执行
fun = function()foo(arg2)
end
-- 执行, 输出2
fun()---------3----------
arg3 = 3
-- 不执行
fun = foo
-- 执行, 输出3
fun(arg3)
进阶: 2个不同来源的传参:
function fooo(arg1, arg2)print(arg1, arg2)
enda = 2
-- 不执行
fun = function(b)fooo(a, b)
end
-- 执行, 输出2,3
fun(3)
二十、死循环
这样写就是死循环↓:
for i = 1, i + 10 doprint(i)
end
正确写法↓:
local endNum = i + 10
for i = 1, endNum doprint(i)
end
二十一、右移: “>>”, 按位与: “&”
print(0x0000ffff)
print(32794992 >> 16)
print(500 & 65535)
print(500 & 63)输出:
65535
500
500
52---------------------------------------------------------------
先看: 0x0000ffff (得: 65535)
16进制的: 0x0000ffff, 就是10进制的: 65535, 就是2进制的: 11111111111111111111(16个1)
(10进制的: 65536, 就是2进制的: 1000000000000000000(16个0), 就是 1>>16)再看: 32794992 >> 16 (得: 500)
10进制的: 32794992, 就是2进制的: 1111101000110100101110000
左移16位, 后面16位就被丢掉了, 只剩下111110100, 就是10进制的: 500
(计算器算 32794992 ÷ 65536 = 500.41186523438)再看 500 & 65535 (得: 500)
一个2进制数, 只要小于16个1, 它和 11111111111111(16个1) 做按位与, 还是它自己
所以 500(111110100) 和 11111111111111(16个1) 做按位与, 还是它自己再看 500 & 63
10进制的: 63, 就是2进制的: 111111
500(111110100) 和 111111 做按位与
就相当于高于6位的都丢掉了, 结果是 110100, 也就是10进制的 52
实例: id是64位, 其组成为:14位没用 - 8位大区id - 10位服务器id - 32位玩家id。
需求: 取服务器id。
分析: 先右移32位, 去除玩家id, 再和1111111111(10个1)做按位与, 就能得到10位的服务器id。
local id = 3063948711696181930
zoneID = (id >> 32) & 1023
print(zoneID)
二十二、左移数值越界
local id = 3063948711696181930
-- 即为2进制的: 10 1010 1000 0101 0101 0101 0001 1110 1010 1010 1010 1010 1010 1010 1010 1010
zoneID = (id << 2)
-- 左移2位, 即后面填2个0, 即为2进制的: 10 1010 1000 0101 0101 0101 0001 1110 1010 1010 1010 1010 1010 1010 1010 1010 00
-- 共64位, 其首位是1, 代表负号
print(zoneID)
-- 所以打印出负数: -6190949226924823896
二十三、可变参数: ‘…’
如果直接用表{…}来接收, 将会丢失传参中的ni。
解决: 用table.pack(…)来接收, 会给一个’n’作为index。
function Foo1(...)-- 用表{...}来接收, 将会丢失传参中的nillocal arg = {...}for k, v in pairs(arg) doprint(v)end
end
Foo1('奥特曼', nil)输出:
奥特曼
----------------------------------function Foo2(...)-- 用table.pack(...)来接收, 会给一个'n'作为indexlocal arg = table.pack(...)for i = 1, arg.n doprint(arg[i])end
end
Foo2('奥特曼', nil)输出:
奥特曼
nil
二十四、浅拷贝表
我预言: 这玩意早晚有一天会坑到我。
list = {name = 1}
temp = list -- 浅拷贝
temp.name = 2 -- temp改变, list也会变
print(list.name) -- 输出2
二十五、字典用table.sort()排序
table.sort()只能排数组, 不能排字典。
解决方法: 把字典放进数组里。
dic = {a = {['id'] = 12, ['order'] = 20001},b = {['id'] = 435, ['order'] = 545},c = {['id'] = 6, ['order'] = 2004501},d = {['id'] = 768, ['order'] = 2},e = {['id'] = 87, ['order'] = 22222}
}array = {}
index = 0
for k, v in pairs(dic) doindex = index + 1v.name = karray[index] = v
end
table.sort(array,function(a, b)return a.order < b.orderend
)
for i = 1, #array dolocal fmt = '%s: name:%s, id:%s, order:%s'print(string.format(fmt, i, array[i].name, array[i].id, array[i].order))
end输出:
1: name:d, id:768, order:2
2: name:b, id:435, order:545
3: name:a, id:12, order:20001
4: name:e, id:87, order:22222
5: name:c, id:6, order:2004501
就排好啦,注意: table.sort()里面只能写小于和大于: < >, 不能带等号: <= =>。
二十六、多条件排序
local tab = {{arg1 = 5, arg2 = 2},{arg1 = 0, arg2 = 5},{arg1 = 2, arg2 = 1},{arg1 = 1, arg2 = 3},{arg1 = 1, arg2 = 4}
}
table.sort(tab,function(a, b)if a.arg1 == b.arg1 then-- 如果条件1相等, 则判断条件2return a.arg2 < b.arg2endreturn a.arg1 < b.arg1end
)
for i = 1, #tab doprint(tab[i].arg1, tab[i].arg2)
end输出:
0 5
1 3
1 4
2 1
5 2
二十七、Lua里没有string.split()
方法1, 会保留nil。
function string.split(str, delimiter)if str == nil or str == '' or delimiter == nil thenreturn nilendlocal result = {}for match in (str .. delimiter):gmatch('(.-)' .. delimiter) dotable.insert(result, match)endreturn result
end-- 测试
local str = "1::3";
local list = string.split(str, ":");
for key, value in pairs(list) doprint(key, value)
end输出:
1 1
2
3 3
方法1, 不保留nil。
function string:split(sep)local sep, fields = sep or "\t", { };local pattern = string.format("([^%s]+)", sep);self:gsub(pattern, function(c) fields[#fields + 1] = c; end)return fields;
end-- 测试
local str = "1::3";
local list = string.split(str, ":");
for key, value in pairs(list) doprint(key, value)
end输出:
1 1
2 3
二十八、lua特殊写法
print (“Hello World”)阔以写成:,print “Hello World”,括号可以被省略。
lua… 你可真是怎么写都行。
二十九、变量名
lua你真是太随便了! 一点都不知道检点!这样写:
table = '奥特曼'
于是table就被重新定义了,true, false, nil 没法重定义,type, string, print 都能被重新定义。
三十、今天星期几
os.date("%w")
1是星期一, 0是星期天
三十一、os.time(): 秒
os.time()没参数, 返回从1970年1月1日到现在的秒数 1604374105
os.time({year=2020,month=1,day=1})返回从1970年1月1日到指定时间的秒数 1577851200
os.time({year=2008,month=1,day=1,hour=8,min=0,sec=0,isdst=false}) 1199145601
其中, 时/分/秒/夏令时 都是可选参数, 不填按0算, 也可以写负数就是算减法
三十二、裆下日期转成表, 再把表转成秒
-- 把裆下日期转化为表: year = 2080 ...
tab = os.date('*t')
tab.hour = 5
tab.min = 20
-- 再把表转化成秒
sec = os.time(tab)
三十三、看身份证满18没有
function IsAdult(idNum)local function CheckYearOld(idNum)local dt = os.date('*t', os.time())local year = tonumber(string.sub(idNum, 7, 10))local month = tonumber(string.sub(idNum, 11, 12))local day = tonumber(string.sub(idNum, 13, 14))if month > dt.month thenreturn dt.year - year - 1elseif month == dt.month and day >= dt.day thenreturn dt.year - year - 1elsereturn dt.year - yearendendreturn CheckYearOld(idNum) >= 18
end
三十四、Lua没有"继承"的弊端
Lua这门语言, 因为没有"继承", 所以连IDE都骗过了
IDE没法给你提示基类的方法, 因为IDE不知道谁是基类
想用基类方法, 你就得掀开基类的衣服, 从头到脚看一遍用过一次就好了
聪明的IDE会记住这个"你曾用过的方法", 并给你提示
三十五、取字符串的长度
string.len()里面可以写数字:
string.len(123456)
#的后面只能跟字符串:
三十六、#号
#号只能取数组的长度, 字典不行。
tab = {[1] = 1,[2] = 1,[3] = 1,[4] = 1,[7] = 1
}
print(#tab)
**你猜多少?输出: 4。
#号的实现原理是什么?**
lua5.2中没有了getn函数。那么常用的取长度方式为#,而#的使用又有些需要注意的地方。
首先要明确的是lua中有两部分:数组部分和hash表部分。而基本上所有操作都是先数组后hash表。
local test1 = { 1 , 2 , 3 , 4 , 5 }
print(#test1)
打印结果: 5local test1 = { 1, 3 , 5 , 2 , 4 }
print(#test1)
打印结果: 5 (好吧。。。。当然跟上面一样,都是作为数组中的值。。。)local test1 = {[1] = 1 , [2] = 2 , [3] = 3 , [4] = 4 ,[5] = 5}
print(#test1)
打印结果: 5 (这里table中没有数组部分了,只有hash表部分)local test1 = {[1] = 1 , [3] = 3 , [4] = 4 , [6] = 6 ,[2] = 2}
print(#test1)
打印结果: 6for i = 1 , #test1 do
print(test1[i])
end
如果全部打印出来, 1 2 3 4 nil 6
明明写的table中只有5个元素,怎么会变成6那。。。。这里的原因就要看下lua源码实现。
/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {unsigned int j = t->sizearray;if (j > 0 && ttisnil(&t->array[j - 1])) {/* there is a boundary in the array part: (binary) search for it */unsigned int i = 0;while (j - i > 1) {unsigned int m = (i+j)/2;if (ttisnil(&t->array[m - 1])) j = m;else i = m;}return i;}/* else must find a boundary in hash part */else if (isdummy(t->node)) /* hash part is empty? */return j; /* that is easy... */else return unbound_search(t, j);
}还是先数组,数组没有后hash部分。再来看下关于hash表部分的取长度static int unbound_search (Table *t, unsigned int j) {unsigned int i = j; /* i is zero or a present index */j++;/* find `i' and `j' such that i is present and j is not */while (!ttisnil(luaH_getint(t, j))) {i = j;j *= 2;if (j > cast(unsigned int, MAX_INT)) { /* overflow? *//* table was built with bad purposes: resort to linear search */i = 1;while (!ttisnil(luaH_getint(t, i))) i++;return i - 1;}}/* now do a binary search between them */while (j - i > 1) {unsigned int m = (i+j)/2;if (ttisnil(luaH_getint(t, m))) j = m;else i = m;}return i;
}
j++保证j是hash部分的第一个值,从j开始,如果j位置是有值的,那么将j扩大两倍,再检查两倍之后hash表中是否可以取到值,直到找到没有值的地方,这个值就在i 到 j这个区间中。然后再用折半查找找到 i 到 j之间找到的最后一个nil的,前面的就是它的长度了。 错略看来。luaH_getint用来取值。
const TValue luaH_getint (Table t, int key)而它的声明看来 ,第二个参数是key,通过key来取value, 而外面对传入的key是++的操作 可知计算长度用来寻找的这个key一定是个整形,而且还得是连续的(不一定)。(当然这个是不深究细节实现错略看下来的分析。。。。。)。
再来验证下:
local test1 = {1 , 3 , [4] = 4 , [6] = 6 ,[2] = 2}
print(#test1)
打印结果: 2
也就是上面源码中,会先遍历数组部分,数组部分有就结束,没有再遍历hash表部分
local test1 = {[4] = 4 , [6] = 6 ,[2] = 2}
print(#test1)
打印结果:0
数组之后的第一位是j++ 如果value是nil, i 是 0 ,j 是1 返回值是0
看两个一起的:
local test1 = {[1] = 1 , [2] = 2 ,[4] = 4 ,[6] = 6}
print(#test1)
local test1 = {[1] = 1, [2] = 2 ,[5] = 5 ,[6] = 6}
print(#test1)
两个的输出结果是6和2 ,而且要是将第一个打印出来 是1 2 3 4 nil 6 中间差一个就能打出来后面的,差两个就不行了 why?就是因为上面源码中得算法。
举个例子:
local test1 = {[1] = 1 , [2] = 2, [3] = 3 ,[4] = 4 ,[6] = 6}
第一个while循环结束, i == 4 ,j == 8, 通过下面的折半查找(具体细节还是拿笔算下吧。。。) 最后i == 6了
而local test1 = {[1] = 1, [2] = 2 ,[5] = 5 ,[6] = 6}
第一个while循环后, i == 2 , j == 4 , 折半查找后 i == 2
恩,就是这样了,如果不清楚这个的话,那么在实际操作的时候,会遇到很奇怪的问题而浪费大量时间。。。。
最后local test1 = { [‘a’] = 1, [‘b’] = 2 ,[‘c’] = 3}
print(#test1)
打印结果: 0 key必须是整形才能用#取。
其他取数组长度形式
如果是字符串或者其他形式的,还是采用循环pairs这种形式去取为好。
三十七、身份证验证
问: 既然有身份证验证的网络接口, 为什么仍需要前端验证呢?
答: 需要先过滤掉一些奇葩(长度不对, 特殊字符, 生日2222年13月32日), 减轻服务器的压力
--验证身份证信息
--只支持18位身份证的验证--[[
#身份证18位编码规则:dddddd yyyymmdd xxx y
#dddddd:地区码
#yyyymmdd: 出生年月日
#xxx:顺序类编码,无法确定,奇数为男,偶数为女
#y: 校验码,该位数值可通过前17位计算获得
#<p />
#18位号码加权因子为(从右到左) Wi = [ 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2,1 ]
#验证位 Y = [ 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2 ]
#校验位计算公式:Y_P = mod( ∑(Ai×Wi),11 )
#i为身份证号码从右往左数的 2...18 位; Y_P为脚丫校验码所在校验码数组位置
参考代码:https://github.com/yujinqiu/idlint
]]
local string_len = string.len
local tonumber = tonumber-- // wi =2(n-1)(mod 11)
local wi = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1 };
-- // verify digit
local vi= { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' }; local function isBirthDate(date)local year = tonumber(date:sub(1,4))local month = tonumber(date:sub(5,6))local day = tonumber(date:sub(7,8))if year < 1900 or year > 2100 or month >12 or month < 1 thenreturn falseend-- //月份天数表local month_days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};local bLeapYear = (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)if bLeapYear thenmonth_days[2] = 29;endif day > month_days[month] or day < 1 thenreturn falseendreturn true
endlocal function isAllNumberOrWithXInEnd( str )local ret = str:match("%d+X?") return ret == str
endlocal function checkSum(idcard)-- copy from http://stackoverflow.com/questions/829063/how-to-iterate-individual-characters-in-lua-stringlocal nums = {}local _idcard = idcard:sub(1,17)for ch in _idcard:gmatch"." dotable.insert(nums,tonumber(ch))endlocal sum = 0for i,k in ipairs(nums) dosum = sum + k * wi[i]endreturn vi [sum % 11+1] == idcard:sub(18,18 )
endlocal err_success = 0
local err_length = 1
local err_province = 2
local err_birth_date = 3
local err_code_sum = 4
local err_unknow_charactor = 5local function verifyIDCard(idcard)if string_len(idcard) ~= 18 thenreturn err_lengthendif not isAllNumberOrWithXInEnd(idcard) thenreturn err_unknow_charactorend-- //第1-2位为省级行政区划代码,[11, 65] (第一位华北区1,东北区2,华东区3,中南区4,西南区5,西北区6)local nProvince = tonumber(idcard:sub(1, 2))if( nProvince < 11 or nProvince > 65 ) thenreturn err_provinceend-- //第3-4为为地级行政区划代码,第5-6位为县级行政区划代码因为经常有调整,这块就不做校验-- //第7-10位为出生年份;//第11-12位为出生月份 //第13-14为出生日期if not isBirthDate(idcard:sub(7,14)) thenreturn err_birth_dateendif not checkSum(idcard) thenreturn err_code_sumendreturn err_success
endlocal function UnitTest_CheckBirthDay()assert(isBirthDate('19881128') == true)assert(isBirthDate('19881328') == false)assert(isBirthDate('19881232') == false)assert(isBirthDate('19880229') == true)assert(isBirthDate('19880228') == true)assert(isBirthDate('18000228') == false)assert(isBirthDate('20000229') == true)assert(isBirthDate('21220228') == false)endlocal function UnitTest()print('begin UnitTest')UnitTest_CheckBirthDay()assert(verifyIDCard('411302198011276412') == err_code_sum)assert(verifyIDCard('4113021988112864x7') == err_unknow_charactor)assert(verifyIDCard('41130219881128641') == err_length)
endUnitTest()
三十八、逻辑运算符的优先级
^
not
* /
+ -
..
> < >= <= ~= ==
and
or
注意: and 优先级大于 or,同时出现and和or时,先执行and,比如:
A or B and C 等同于 A or (B and C)
还要注意: not 的优先级大于 ==,比如:
print(1 == 2) -- false
print(not 1 == 2) -- 还是false
-- 都是false, 我还以为是Lua或者VSCode出bug了
-- 其实是因为not优先
print(not (1 == 2)) -- true
三十九、虚变量
(虚设变量/ 虚拟变量/ 名义变量/ 哑元变量/ 哑变量 叫啥玩意都行)。
t = {'迪迦', '戴拿', '蒂格', '蒂纳'}
for k, v in pairs(t) doprint(v)
end
这时, 我们只需要v, 而不需要k,这时, 按照约定俗成, 把 k 写成 _。
↓此写法表示: 我不需要理会k这个变量 , 即, 虚变量。
t = {'迪迦', '戴拿', '蒂格', '蒂纳'}
for _, v in pairs(t) doprint(v)
end
四十、匹配字母和数字
function isMatch(str)match = string.match(str, '%w+')return str == match
end
四十一、if not a
不用写 if a == false or a == nil then
直接写 if not a then 就好了
四十二、墙裂推荐一个VS Code的插件: Rainbow Fart
你敲代码时候, 她会说话敲个if, 她说: 你就是因为想太多"如果", 才找不到女朋友的
现在是晚上9点, 她说: 怎么还没下班呀
四十三、数组洗牌打乱
function shuffle(tbl)local n = #tblfor i = 1, n dolocal j = math.random(i, n)if j > i thentbl[i], tbl[j] = tbl[j], tbl[i]endend
end
四十四、JavaBean的要求规范
- 1、所有属性为private
- 2、提供默认构造方法
- 3、提供getter和setter
- 4、实现serializable接口
这是为了向后兼容, 改的时候只改方法内部就好了,便于封装重用, 便于让其他程序员或者框架使用。
四十五、实现Switch
记得多看几遍这个myMetaTable干了什么。
table[4] = print('我是4')local table = {[1] = print('我是1'),a = print('我是a'),b = print('我是b'),__default = print('我是default')
}myMetaTable = {__index = function(t, k)print(t == table, k)return rawget(t, '__default')end
}setmetatable(table, myMetaTable)function switch(case)return table[case]
endtable.c = print('我是c')switch(1)
switch(4)
switch(a)
switch('b')
switch('c')
switch(d)
print('卧槽, 这Lua的调用顺序好乱啊')输出:我是4
我是1
我是a
我是b
我是default
我是c
true 1
true 4
true nil
true b
true c
true nil
卧槽, 这Lua的调用顺序好乱啊
四十六、实现continue
思路: 用一次性while里的if-break作为continue。
for i = 1, 10 dowhile true doif i > 3 and i < 8 thenbreakendprint(i)breakend
end
输出:
1 2 3 8 9 10
四十七、string.format
必须记住的: %d: 整数, %f: 浮点数, %s: 字符串。其他的不太重要, 等用到的时候再查。
0.前面加加号: "+"
print(string.format("%+d", 1234))
输出: +12341.前面补0: "08d"
string.format("%08d", 1234))
输出: 000012342.截取小数, 4舍5入: ".3f"
string.format("%.3f", math.pi)
输出: 3.1423.截取字符串: ".3s"
string.format("%.3s", "abcdefg")
输出: abc
四十八、三元表达式
(在Lua中false和nil为假, 其他都是真, 0也是真, {}也是真),(a and b: 如果a假, 则返回a, 否则返回b),(a or b: 如果a真, 则返回a, 否则返回b)。
C#的三元表达式ret = a ? b : c 在Lua中可以写成:
ret = a and b or c
但是, 上式中b一定不能为假, 若b为假, 就会始终返回c。
解决: 把b放进表里, 因为{false}为真, {nil}也为真。
注意: 不能把a放进表里, 那样的话a恒为真, 会始终返回b。
最终, 严谨的写法为:
ret = (a and {b} or {c})[1]
当然, 当我们确认b不可能为假时, 就可以直接写: ret = a and b or c
四十九、"逻辑与"的截断机制
在C#里:
a && b
先判断a是否为真
如果a为真, 再判断b是否为真, b为真则返回true, b为假则返回false
如果a为假, 就截断了, b不看了, 直接返回false在Lua里:
a and b
先判断a是否为真
直接截断!
b不看了, a为假则返回a, a为真则返回b可以看出, lua比C#更省劲
五十、问题:Lua的输出乱码
输出为: ‘lua‘ �����ڲ����ⲿ���Ҳ���ǿ����еij��� ���������ļ���
解决: 改系统字体为 utf-8, 控制面板 区域设置:Bate版UTF8全球支持
五十一、问题:Lua不是内部或外部命令,也不是可运行的程序或批处理文件
输出为: ‘lua‘ is not recognized as an internal or external command, operable program or batch file
解决:下载Lua,配Lua的环境变量:将Lua.exe所在路径添加至系统变量的Path中
五十二、判断table里有没有某元素
判断表里有没有一个value, 需要遍历表, 挨个判断是否相等。
判断表里有没有一个key, 只需要: return t[key] ~= nil。
需要注意的是判断空表:
--这样是不行的
if a == {}
--要这样:
if next(a) == nil
弹出表中的下一个元素, 结果它弹出来个nil, 不就证明了是空表嘛。
五十三、(不)连续表的遍历
连续表(类似数组)遍历用 ipairs,
非连续表(类似字典)遍历用 pairs,
五十四、正则表达式
ua的正则表达式:
string.gmatch(%d+)
五十五、取模和取余
正好前几天教二年级数学,被除数 ÷ 除数 = 商 … 余数。
local line = math.modf(9 / 4) -- 取整数
local mod = math.fmod(9, 4) -- 取模
local remain = 9 % 4 -- 取余数
在数学中, 取模和取余基本上说的是同一个事,在计算机中, 却不大一样 (在有负号的时候)。
print('取模: ' .. math.fmod(9, 4))
print('取余: ' .. 9 % 4)
print('---------------')
print('取模: ' .. math.fmod(-9, 4))
print('取余: ' .. -9 % 4)
print('---------------')
print('取模: ' .. math.fmod(9, -4))
print('取余: ' .. 9 % -4)输出:
取模: 1
取余: 1
---------------
取模: -1
取余: 3
---------------
取模: 1
取余: -3
五十六、#汉字长度
一个汉字占3个长度。
print(#"四个汉字")输出:
12
五十七、赋值多个值
a, a= 3, 4, 5
print(a)输出:
3
五十八、交换2个值
不需要开辟额外的temp空间, 也不用巧妙的加减运算或是位运算,Lua的写法贼简单, 直接 a, b = b, a。
a = 1
b = 2
a, b = b, a
交换数组的元素也可以。
arr = {'a', 'b', 'c'}
arr[1], arr[3] = arr[3], arr[1]
for i, v in pairs(arr) doprint(i .. v)
end输出:
1c
2b
3a
五十九、返回多个值
function fun()return 1, 2, 3
end
print("额", fun())
print(fun(), "额") -- 因为fun不是最后一个, 所以只返回一个值
print((fun())) -- 强制仅返回一个值输出:
额 1 2 3
1 额
1
六十、方法后面不加小括号
a = aaa{"a", "b"}
看到这个写法我当时蒙了, 这是什么写法? 表? 不对啊, 它为什么不报错呢?哦, 原来aaa是个方法啊。
function aaa(t)print(t[1])
end
--因为传参是table, 所以可省略小括号
--正常写法为:
a = aaa({2, 3})
--省略写法为:
a = aaa {2, 3}
六十一、按位运算
a~b 按位异或
~a 按位取反
六十二、// 运算, 取商
不知道为什么, 网上查不到这个操作符号"//",我猜是取商运算。
print(5 // 2)
print(5.0 // 2)
print(5 // 2.0)输出:
2
2.0
2.0
注意: 当被除数或除数为浮点数时, 结果也是浮点数
学习Lua碰到的问题、踩坑记录相关推荐
- cesium给地图添加比例尺学习踩坑记录
cesium给地图添加比例尺学习踩坑记录 因项目需要在cesium地图中展示比例尺,本来应该是很简单的事,但却碰到了一个引用文件的坑,特此记录: *1.引用依赖文件 相信需要用到cesium比例尺组件 ...
- 【学习记录】QT5界面设计的踩坑记录
学习记录:QT5 界面设计的踩坑记录 前言 一.Qlabel显示视频与图片 1. 图片显示 1.1 显示格式 1.2 label随界面缩放 1.3 界面刷新 2. 视频显示 二.常见控件的StyleS ...
- AirSim学习和踩坑记录(不定时更新)
版权声明:本文为博主原创文章,遵循Creative Commons - Attribution-ShareAlike 4.0 International - CC BY-SA 4.0版权协议,转载请附 ...
- MAC-XXL_JOB学习踩坑记录-Failed to create parent directories for [/data/applogs/xxl-job/xxl-job-admin.log
MAC-XXL_JOB学习踩坑记录 源码下载地址 启动报错 源码下载地址 ①.GitHub:https://github.com/xuxueli/xxl-job ②.码云:https://gitee. ...
- Slam学习笔记——ROS踩坑记录
Slam学习笔记--ROS踩坑记录 1. 安装 2. ROS文件系统 2.1 工作区 2.2 包package 2.2.1 包的操作 2.2.2 描述文件package.xml 2.3 节点node ...
- 双系统Ubuntu22.04深度学习环境配置与踩坑记录
双系统Ubuntu22.04深度学习环境配置踩坑记录 前言 目录 相关版本 主要参考教程 Ubuntu安装 Nvidia和CUDA安装 踩坑经历 官网安装所遇问题 cuDNN安装 Anaconda安装 ...
- mybatis学习与踩坑记录
mybatis resultmap高级映射 应用场景:如果sql查询的列名和pojo的属性名不一致,可以使用resultMap将列名和pojo的属性名作一个对应关系,就可以映射成功了.(如果返回值为i ...
- 【踩坑记录】仿真环境使用小车进行Cartographer 3D Slam(深度摄像头)
[运行背景] ROS1 20.04 noetic 安装cartographer请看: [安装学习]安装Cartographer ROS(noetic)_Howe_xixi的博客-CSDN博客网上使用n ...
- ROS noetic 安装编译Cartographer踩坑记录
ROS noetic 安装编译Cartographer踩坑记录 安装编译工具 sudo apt-get update sudo apt-get install -y python-wstool pyt ...
- 【踩坑记录】实体机器人运行Cartographer 3D Slam(深度摄像头)--未解决
[运行背景] ROS1.0 20.04 noetic 机器人:NXRobo SPARK-T 安装cartographer请看: [安装学习]安装Cartographer ROS(noetic)_Ho ...
最新文章
- android的shadowRadius属性说明
- Python基础教程:列表解析
- 项目常用第三方库 Swift版
- [js高手之路]this知多少
- python继承方式是基于原型吗_基于原型与基于类的继承
- 七步从Angular.JS菜鸟到专家(2):Scopes
- chage 用户密码管理
- 信息传递(NOIP2015提高组Day1T2)
- 给还是不给?又一个国家要求苹果必须为iPhone 12提供充电器
- golang 单协程和多协程的性能测试
- 中国农历2013,2014 (zz.IS2120@BG57IV3)
- 2492 上帝造题的七分钟 2
- 0618----Shell(二)
- 如何使用fiddler抓取APP接口
- HyperLynx(三)传输线类型及相关设置
- 如何将App程序发布到苹果App Store
- Java bean 复制克隆工具
- CC00034.bigdatajava——|Java方法封装.V16|——|Java.v16|费氏数列.v02|递推实现|
- java高级架构师年薪,深度集成!
- intelx79服务器芯片组,流言终结者!Intel X79规格全面介绍
热门文章
- java版超级玛丽游戏
- 有选择的忽略PyCharm 3的PEP8语言风格警告提示信息
- mapper找不到报错:Field xxxMapper in xxx required a bean of type 'xxxMapper' that could not be found
- Java面试之Java基础5——面向对象的三大特性之封装、继承和多态
- 为了让机器听懂“长篇大论”,阿里工程师构建了新模型
- jsp左侧菜单栏_HTML页面左侧菜单栏切换实现右侧主体内容改变
- 【C语言学习】————操作符、关键字
- 紫罗兰计算机音乐,【Animenz】紫罗兰永恒花园ed みちしるべ
- macOs Ventura 13自动开机关机设置教程(命令行)
- Android欢迎页面以及引导页面