03-字符串
Lua语言中的字符串可以表示一或多个字符,它由一串字节组成,每个字符使用8个位存储。字符串可以存储包括空字符在内的所有数值代码,也就是说可以在字符串中存储任意的二进制数据。此外,从Lua5.3开始提供处理UTF-8编码的函数库。
Lua中的字符串是 不可变值,这说明不能直接改变字符串的某个字符,应该再创建一个新的字符串来达到“修改”的目的:
a = "one string"
b = string.gsub(a, "one", "another") -- 把a中的"one"改成"another"
像Lua语言中的其他对象(表、函数等)一样,Lua的字符串也是自动内存管理的对象之一,开发人员无需关注内存的分配和释放问题。
常用操作
声明字符串
可以用一对双引号或单引号来声明字符串常量,用单引号声明的常量不用转义双引号;用双引号声明的常量不用转义单引号。
a = "line"
b = 'another line'
Lua语言中的字符串支持如下常见的C风格转义字符:
| 转义字符 | 释义 |
|---|---|
\a | 响铃 |
\b | 退格 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 反斜杠 |
\" | 双引号 |
\' | 单引号 |
除此之外,还能通过形如\ddd和\xhh的转义序列来声明字符,前者是由最多3个10进制数字组成的序列,后者是由且仅由2个16进制数字组成的序列。从Lua5.3开始,也能用转义序列\u{16进制数字}来声明UTF-8字符。
字符串运算符
可以用 长度操作符# 获取字符串的长度:
a = "hello"
#a --> 5
#"good bye" --> 8
可以用 连接操作符.. 来进行字符串连接:
"Hello " .. "World" --> Hello World
"res = " .. 3 --> res = 3
长字符串/多行字符串
像多行注释一样,也能用一对双括号来声明多行字符串:
page = [[
<html>
<body> hello </body>
</html>
]]
被方括号括起来的内容可以包括很多行,并且内容中的转义序列不会被转义。此外如果多行字符串中的第一个字符是换行符,那么这个换行符不会被包含在字符串中。
如果字符串内部也有]]可能会发生冲突,可以通过在注释的双括号间加入等量的=进行区分:[==[xxx]==],长注释也能这样做。
强制类型转换
Lua在运行时提供了数值类型与字符串类型之间的自动转换,针对字符串的所有算术操作都会尝试将字符串转换为数值。同理,当Lua发现在需要字符串的地方出现数值时,也会尝试将数值转换为字符串。
除此之外,可以用函数tonumber()将字符串转换为数值,用函数tostring()将数值转换为字符串。
tonumber(" -3 ") --> -3
tonumber("fff", 16) --> 4095
tostring(10) == "10" --> true
与算术操作不同,在进行比较操作时不会对操作数进行强制类型转换,例如"0"和0是不同的值。因此为了避免出现意外的结果,这种情况下Lua会抛出异常。
字符串标准库
Lua语言处理字符串的完整能力来自其字符串标准库。
常用库函数
string.len(s)
返回字符串s的长度,等价于 #s。
string.rep(s, n)
返回字符串s重复n次的结果。
string.reverse(s)
返回将字符串s翻转后的结果。
string.lower(s)
返回字符串s的小写版本。
-- 忽略大小写比较字符串
string.lower(a) < string.lower(b)
string.upper(s)
返回字符串s的大写版本。
string.sub(s, i, j)
返回字符串s的子串,范围是[i, j],其中字符串的第一个索引为1。索引可以是负数,此时它代表倒数第x个字符。
string.char(i, ...)
接收若干整数作为参数,然后将每个整数转换为对应的字符,最后返回由这些字符连接而成的字符串。
string.char(99, 100, 101) --> cde
string.byte(s, i, j)
返回字符串s中区间[i, j]的字符的数值:
string.byte("abc", -1) --> 99
当字符串不大时(不超过1MB),可以用{string.byte(s, 1, -1)}将字符串s中所有字符转换为所有字符数值组成的表,因为栈大小有限制,限制了该函数的返回值,因此最大为一百万个字符数值。
格式化输出
string.format()
是用于进行字符串格式化和将数值格式化输出为字符串的强大工具,该函数会返回第一个参数(格式化字符串)的副本,其中的每一个指示符都会被替换为使用对应格式进行格式化后的对应参数。使用和C语言的printf()类似:
string.format("x = %d y = %d", 10, 20) --> x = 10 y = 20
string.format("x = 0x%X", 200) --> x = 0xC8
tag, title = "h1", "a title"
string.format("<%s>%s</%s>", tag, title, tag) --> <h1>a title</h1>
模式匹配
string.find()
用于在指定的字符串中进行模式搜索:
string.find("hello world", "wor") --> 7 9
string.find("hello world". "war") --> nil
如果找到了,会返回开始和结束位置;找不到则返回nil。
string.gsub()
把所有匹配到的模式用另一个字符串替换:
string.gsub("hello world", "l", ".") --> he..o wor.d 3
string.gsub("hello world", "a", ".") --> hello world 0
返回“修改”后的新字符串,以及发生替换的次数。
string.match()
和string.find()类似,但返回的是与模式匹配的子串,而不是位置索引:
string.match("hello world", "hello") --> hello
string.match("Today is 17/7/1990", "%d+/%d+/%d+") --> 17/7/1990
其中,参数"%d+/%d+/%d+"被称作模式,有关模式,捕获与替换的笔记将在后续补充 TODO。
string.gmatch()
返回一个迭代器函数,通过调用该函数可以遍历字符串中所有符合模式的子串:
-- 找出字符串s中出现的所有单词
s = "some string"
words = {}
for w in string.gmatch(s, "%a+") do
words[#words + 1] = w
end
for循环会遍历目标字符串中所有单词,并将它们存储到列表words中。
冒号操作符
如果s是一个字符串,那么可以用 冒号操作符: 调用字符串标准库的任意函数:
s:sub(i, j) --> string.sub(s, i, j)
s:upper() --> string.upper(s)
Unicode编码
从Lua5.3开始,Lua语言引入了一个用于操作UTF-8编码的Unicode字符串的标准库。UTF-8使用变长的多个字节来编码一个Unicode字符。例如,UTF-8使用一个字节的65来代表A,使用两个字节的215-144代表希伯来语字符Aleph。
因为函数reverse,upper,lower,byte,char针对单字节字符处理,所以这些函数不能用于处理UTF-8字符串。而Lua提供了utf8库来解决这些函数无法使用的问题。
utf8库
在utf8库中:
utf8.len()和string.len()类似,不过它返回的是UTF-8字符的代码点个数,如果有无效字符,会返回nil和无效字符位置。utf8.char(),utf8.codepoint()在UTF-8环境下等价于string.char()和string.byte()。- 需要注意的是,
utf8库中 大多数函数使用字节作为索引,而不是字符位置。如果要用字符位置作为索引,需要用utf8.offset(s, i)获取第i个字符的字节位置。 - 使用
utf8.codes()获取UTF-8字符串所有字符的字节位置与内容,便于遍历这些信息。
如果要支持其他特殊需求,得使用外部库。
参考资料
- 《Lua程序设计 (第4版)》
- 【唐老狮】Unity热更新之Lua语法