楔子
任何一门语言都提供了不同类型的数据结构,那么 Lua 中都有哪些数据结构呢?
nil:空
boolean:布尔类型,分别是 true 和 false
number:数值类型,整型和浮点型都属于 number
string:字符串
table:表
function:函数
userdata:用户数据
thread:线程
Lua总共提供了以上 8 种数据类型,目前只需要知道一下即可,我们先将一些基础概念等前置工作说一下,后面会一点一点地慢慢介绍。
然后是 Lua 的关键字,总共有 22 个:
and break do else elseif end false goto for function if
in local nil not or repeat return then true until while
这些关键字不用刻意去记,因为还是那句话,既然学习 Lua,肯定有其它编程语言基础,所以这些关键字显然是大部分都见过的。至于不认识的关键字,我们后面也会慢慢遇到。
最后再补充一下 Lua 的注释,Lua 里面也分为单行注释和多行注释。单行注释和 SQL 一样,使用两个减号:
-- 这是单行注释
多行注释以 --[[ 开头,以 ]] 结尾,里面写注释。
--[[
这是多行注释
并且开头的 -- 和 [[ 之间不可以有空格,结尾是两个 ]
]]
下面这种写法也是多行注释,不是两行单行注释。
--[[
这也是多行注释
不是两行单行注释
--]]
下面我们就来聊一聊 Lua 的基本类型。
数值类型
Lua 中的数值类型为 number,整型和浮点型都是 number。
-- Lua 和 Python 类似,在创建变量时不需要指定类型
-- 解释器会自动根据赋的值来判断
a = 123
b = 3.14
print(a, b) -- 123 3.14
-- Lua中,每一行语句的结尾也不需要加分号,直接换行即可
-- 当然加分号也是可以的,跟 Python 是类似的
c = 123;
d = 456
print(c, d) -- 123 456
-- 并且在 Python 中,如果加上了分号,那么两行赋值可以写一行
-- 比如 e = 1; f = 2
-- 这在 Lua 中也是可以的
e = 1; f = 2
print(e, f) -- 1 2
-- 但是 Lua 比较彪悍的是,不加分号也可以
-- 如果在 Python 中这么写,则肯定是报错的
g = 3 h = 4
print(g, h) -- 3 4
-- 但是我们不建议将多行赋值语句写在同一行里面,最好要分行写
-- 当然在 Lua 中也可以使用多元赋值
a, b = 1, 2
print(a, b) -- 1 2
这里可能有人发现了,我们在最上面已经创建 a 和 b 这两个变量了,但是下面又创建了,这一点和 Python 类似,可以创建多个同名变量。比如创建 a = 1,然后又创建 a = 2,这是允许的。只不过这相当于发生了更新,将 a 的值由 1 变成了 2,当然即便赋值为其它类型也是可以的。
因为在 Lua 中,全局变量是通过 table、也就是"表"来存储的。这个 table 后面会详细说,你暂时可以理解为哈希表,或者当成 Python 中的字典,而且 Python 中全局变量也是通过字典存储的。
补充:数值还可以使用 16 进制表示,比如 0x333。
我们通过 Lua 的数值类型,来演示了如何在 Lua 中如何创建一个变量,并且还介绍了 Lua 中全局变量的存储方式,下面再来看看如何区分整型和浮点型。
a = 123
b = 123. -- . 后面不写东西的话,默认是 .0
c = .123 -- . 前面不写东西的话,默认是 0.
print(a, b, c) -- 123 123.0 0.123
-- Lua 中,可以使用 type 函数检测变量的类型
print(type(a)) -- number
print(type(b)) -- number
print(type(c)) -- number
-- 这个 type 是内置的,它检测的是 Lua 中的基础类型
-- 而我们说 Lua 不区分整型和浮点型,如果想精确区分的话,那么可以使用 math.type
-- 整型是 integer,浮点型是 float
print(math.type(a)) -- integer
print(math.type(b)) -- float
print(math.type(c)) -- float
-- 如果一个数值中出现了小数点,那么 math.type 得到的就是 float
使用 type 和 math.type 得到的都是一个字符串,另外我们是直接使用的 math.type,这个 math 是哪里来的。其实 Lua 解释器内置了很多包,我们直接用即可,并且也不需要像 Python 那样使用之前先导入。
整数和浮点数的比较print(3 == 3.0) -- true
print(-3 == -3.0) -- true
-- 我们看到,如果小数点后面是 0,那么也是相等的,这一点和 Python 是一样的
-- Lua 也支持如下方式创建浮点数
print(3e3) -- 3000.0
-- 但是我们看到,得到是浮点
-- 在 Lua 中,只要十进制数中出现了小数点、或者出现了幂运算、除法运算,那么得到的都是一个浮点
-- 准确的说,math.type 检测的结果是 float,因为 Lua 中不区分整型和浮点,它们都是 number
-- 这里我们说浮点只是为了方便,理解意思即可
-- Lua中 a ^ b 表示 a 的 b 次方
-- 如果运算中出现了浮点数,或者发生了幂运算,那么结果就是浮点
print(3 ^ 2) -- 9.0
print(3 * 3) -- 9
在 Lua 中,只要十进制数中出现了小数点、或者出现了幂运算,那么得到的都是一个浮点数。准确的说,math.type 检测的结果是 float,因为 Lua 中不区分整数和浮点数,它们都是 number。
算术运算符算数运算没啥好说的,总共有 8 个。
+(相加)
-(相减)
*(相乘)
/(相除)
//(地板除)
%(取模)
^(幂运算)
~(取反)
对于相加、相减、相乘、取模,如果是两个整型,那么结果还是整型,如果出现了浮点,那么结果为浮点。
print(1 + 2, 1 + 2.) -- 3 3.0
print(1 * 2, 1 * 2.) -- 2 2.0
print(1 - 2, 1 - 2.) -- -1 -1.0
print(13 % 5, 13. % 5) -- 3 3.0
如果相除,那么结果一定为浮点。
print(3 / 2, 4 / 2, 4 / 2.) -- 1.5 2.0 2.0
当然在 Lua 中,还有一个地板除,会对商向下取整,这是在 Lua5.3 中引入的。
print(3 // 2, 4 // 2) -- 1 2
-- 如果里面出现了浮点,那么即使是地板除,也一样会得到小数
print(4 // 2.) -- 2.0
-- 虽然是浮点,但我们看到的是 1.0, 相当于还是有向下取整的效果
print(4 // 2.5) -- 1.0
当然 Lua 中还有幂运算,使用 ^ 表示。
print(3 ^ 4) -- 81.0
print(3e4) -- 30000.0
只要出现了幂运算,得到的一定是浮点。最后是取反,使用波浪号:
print(~8) -- -9
print(~-7) -- -6
关于整数取反, 有一个快速记忆的方式:对整数 x 的取反结果为 (x + 1) * -1。
按位运算符按位运算符有 5 个,分别是:
&(按位与)
|(按位或)
~(按位异或、取反)
<<(左移)
>>(右移)
-- 按位与 &
print(15 & 20) -- 4
-- 按位或 |
print(15 | 20) -- 31
-- 按位异或 ~,异或在 Python 中是 ^,但 ^ 在 Lua 是幂运算
print(15 ~ 20) -- 27
-- 取反,取反的话也是 ~
print(~20) -- -21
-- 左移
print(2 << 3) -- 16
-- 右移
print(16 >> 2) -- 4
以上这些操作符是在 5.3 当中才提供的,如果是之前的版本,则不能使用这些操作符。
比较运算符比较运算符又叫关系(Relational)运算符,一共有 6 个,分别是:
==(等于)
~=(不等于)
>(大于)
>=(大于等于)
<(小于)
<=(小于等于)
这个比较简单,没什么好说的,但注意:不等于在 Lua 里面是 ~=,而非 !=。
逻辑运算符逻辑运算符有 3 个,分别是:and(逻辑与)、or(逻辑或)、not(逻辑非)。Lua 语言没有给逻辑运算符分配专门的符号,而是给它们分配了三个关键字,这一点和 Python 是一样的。
需要注意的是,除了 false 和 nil 会被视为假之外,其它值一律为真。
数学库Lua 也提供了一个数学库,叫做 math,除了可以用它查看数值的具体类型,里面还定义了很多用于数学计算的高级函数,比如:sin、cos、tan、asin、floor、ceil 等等。
print(math.sin(math.pi / 6)) -- 0.5
比较简单,更多的函数可以自己查看。
布尔类型
再来看一下 Lua 的布尔类型,该类型只有两个值:true 和 false。除了 nil 和 false 的布尔值为 false 之外,其它值的布尔值均为 true。那么如何得到一个布尔值呢?
-- 直接创建布尔值
flag1 = true
flag2 = false
print(flag1, flag2) -- true false
-- 将某个值转成布尔值
num = 0
print(not not num) -- true
-- 调用两次 not 即可得到布尔值,这在 Python 中也是可以的
-- 只不过在 Python 里面还可以使用内置的 bool 函数,但 Lua 里面则没有这个函数
再次强调:像 0、空字符串等,在 Lua 里面会被视为真值,只有 nil 和 false 为假,其它均为真。
字符串
下面我们看看 Lua 的字符串,字符串既可以使用双引号、也可以使用单引号。注意:Lua 的字符串是不可变对象,不能本地修改,只能创建新的字符串。
name = "komeiji satori"
print(name) -- komeiji satori
-- 使用 # 可以获取其长度
print(#name, #"name") -- 14 4
-- 使用 .. 可以将两个字符串连接起来
print("aa" .. "bb") -- aabb
print("name: " .. name) -- name: komeiji satori
-- .. 的两边可以没有空格,但是为了规范,建议前后保留一个空格
-- .. 前后还可以跟数字,会将数字转成字符串
print("abc" .. 3, 3 .. 4, 3 .. "abc") -- abc3 34 3abc
-- 另外如果 .. 的前面是数字的话,那么 .. 的前面必须有空格
-- 也就是写成类似于 3 .. 的形式 不可以写 3..
-- 因为 3 后面如果直接出现了. 那么这个 . 会被当成小数点来解释
Lua 内部也支持多行字符串,使用[[
和]]
表示。
msg = [[
你好呀
你在什么地方呀
你吃了吗
]]
print(msg)
--[[
你好呀
你在什么地方呀
你吃了吗
]]
字符串和数值的转换
Lua 的字符串可以和数值相加,也可以相互转换。
print("10" + 2) -- 12.0
-- 如果字符串和整数运算,那么得到的也是浮点
-- 你可以认为只有整数和整数运算才有可能得到整数,而字符串不是整数
-- 调用 tonumber 可以将字符串显式的转为整数
print(type(tonumber("10"))) -- number
print(tonumber("10") + 2) -- 12
-- 如果转化失败,那么结果为 nil
print(tonumber("ff")) -- nil
-- 当然有些时候我们的数字未必是 10 进制,比如上面的 ff,它可以是 16 进制
-- 如果需要进制,那么就给 tonumber 多传递一个参数即可
print(tonumber("ff", 16)) -- 255
print(tonumber("11101", 2)) -- 29
print(tonumber("777", 8)) -- 511
-- 8 进制,允许出现的最大数是 7,所以转化失败,结果为nil
print(tonumber("778", 8)) -- nil
-- 整型转化成字符串,则是tostring
print(tostring(100) == "100") -- true
print(tostring(100) == 100) -- false
我们看到整数(还有浮点数)和字符串是可以相加的,当然相减也可以,会将字符串转成浮点数进行运算。也可以判断是否相等或者不相等,这个时候会根据类型判断,不会隐式转化了,由于两者类型不一样,直接不相等。
但整数和字符串两者是无法比较大小的,只能判断是否相等或者不等,因为 2 < 15 ,但是 "2" > "15"。所以为了避免混淆,在比较的时候 Lua 不会隐式转化、加上类型不同也无法比较大小,因此直接抛异常。
字符串标准库Lua 处理字符串还可以使用一个叫做 string 的标准库,这个标准库在 5.3 中也是内嵌在解释器里面的,我们直接通过 string.xxx 即可使用。下面就来看看 string 这个标准库都提供了哪些函数吧,这里说一下 Lua 的字符串是以字节为单位,而不是以字符为单位。因此 string 的大部分函数不适合处理中文(也有少数例外),如果是中文,可以使用后面要介绍的 utf8。
-- 查看字符串的长度
print(string.len("abc"), #"abc") -- 3 3
-- 一个汉字占三个字节,默认是以字节为单位的,计算的是字节的个数
print(string.len("古明地觉"), #"古明地觉") -- 12 12
-- 重复字符串 n 次
print(string.rep("abc", 3)) -- abcabcabc
-- 如果是单纯的重复字符串的话,也可以对中文操作,因为不涉及字符的局部截取
print(string.rep("古明地觉", 3)) -- 古明地觉古明地觉古明地觉
-- 字符串变小写,可以用于中文,但是没意义
print(string.lower("aBc")) -- abc
print(string.lower("古明地觉")) -- 古明地觉
-- 同理还有转大写
print(string.upper("aBc")) -- ABC
print(string.upper("古明地觉")) -- 古明地觉
-- 字符串翻转,这个不适合中文
print(string.reverse("abc")) -- cba
print(string.reverse("古明地觉")) -- ��谜厘椏�
-- 我们看到中文出现了乱码,原因就是这个翻转是以字节为单位从后向前翻转
-- 而汉字占 3 个字节,需要以 3 个字节为单位翻转
-- 字符串截取,注意:Lua 中索引是从 1 开始的
-- 结尾也可以写成 -1,并且字符串截取包含首位两端
print(string.sub("abcd", 1, -1)) -- abcd
print(string.sub("abcd", 2, -2)) -- bc
-- 可以只指定开头,不指定结尾,但是不可以开头结尾都不指定
print(string.sub("abcd", 2)) -- bcd
-- 同样不适合中文,除非你能准确计算字节的数量
print(string.sub("古明地觉", 1, 3)) -- 古
print(string.sub("古明地觉", 1, 4)) -- 古�
-- 超出范围,就为空字符串
print(string.sub("古明地觉", 100, 400) == "") -- true
-- 将数字转成字符
print(string.char(97)) -- a
-- 如果是多个数字,那么在转化成字符之后会自动拼接成字符串
print(string.char(97, 98, 99)) -- abc
-- 字符转成数字,默认只转化第1个
print(string.byte("abc")) -- 97
-- 可以手动指定转化第几个字符
print(string.byte("abc", 2)) -- 98
print(string.byte("abc", -1)) -- 99
-- 超出范围,那么返回 nil
print(string.byte("abc", 10) == nil) -- nil
-- 转化多个字符也是可以的,这里转化 1 到 -1 之间的所有字符
print(string.byte("abc", 1, -1)) -- 97 98 99
-- 越界也没事,有几个就转化几个
print(string.byte("abc", 1, 10)) -- 97 98 99
-- 另外,这里是返回了多个值,我们也可以用多个变量去接收
a, b, c = string.byte("abc", 1, 10)
print(a, b, c) -- 97 98 99
-- 关于 Lua 返回值,这涉及到了函数,我们后面会说
-- 字符串的格式化,格式化的风格类似于C
print(
string.format("name = %s, age = %d, number = %03d", "古明地觉", 17, 1)
) -- name = 古明地觉, age = 17, number = 001
-- 字符串的查找,会返回两个值,分别是开始位置和结束位置
print(string.find("abcdef", "de")) -- 4 5
-- 不存在则为 nil
print(string.find("abcdef", "xx")) -- nil
-- 字符串的全局替换,这个替换可以用中文
-- 返回替换之后的字符串和替换的个数
print(string.gsub("古名地觉 名名 那么可爱", "名", "明")) -- 古明地觉 明明 那么可爱 3
-- 我们同样可以使用返回值去接
new_str, count = string.gsub("古名地觉 名名 那么可爱", "名", "明")
print(new_str) -- 古明地觉 明明 那么可爱
关于处理 ASCII 字符,string 库为我们提供了以上的支持,不难发现支持的东西还是比较少的,因为 Lua 的源码总共才两万两千多行,所以这就决定了它没办法提供过多的功能。Lua主要还是用来和别的语言结合的,然而事实上,string 库提供的东西也不少了。
下面来看看 utf8,我们说 string 库不是用来处理 unicode 字符的,如果处理 unicode 字符的话,需要使用 utf8 这个库
-- Lua 中存储 unicode 字符使用的编码是 utf-8
-- 计算长度
print(utf8.len("古明地觉")) -- 4
-- 类似于 string.byte,这两个可以通用
print(utf8.codepoint("古明地觉", 1, -1)) -- 21476 26126 22320 35273
-- 类似于 string.char,这两个可以通用
print(utf8.char(21476, 26126, 22320, 35273)) -- 古明地觉
-- 截取,使用 string.sub
-- 但使用 utf-8 编码,不同字符占的字节大小可能不一样,这时候怎么截取呢
-- 可以先通过 utf8.offset 计算出,偏移到第 n 个字符的字节量
print(string.sub("古明地觉", utf8.offset("古明地觉", 2))) -- 明地觉
print(string.sub("古明地觉", utf8.offset("古明地觉", -2))) -- 地觉
-- 遍历,遍历使用了 for 循环,我们后面说,现在先看一下
for i, c in utf8.codes("古a明b地c觉") do
print(i, c, utf8.char(c))
--[[
1 21476 古
4 97 a
5 26126 明
8 98 b
9 22320 地
12 99 c
13 35273 觉
]]
end
以上便是 Lua 处理字符串的一些操作, 尽管功能提供的不是非常的全面,但这与 Lua 的定位有关。
到目前为止,我们就介绍了 Lua 的数值、布尔值和字符串,现在使用的层面了解它,后面我们会分析它的底层实现。
标签:string,--,数据类型,Lua,聊一聊,字符串,print,地觉 From: https://www.cnblogs.com/traditional/p/17506501.html