.# 【Lua程序设计】字符串
Lua语言中的字符串是一串字节组成的序列。在Lua语言中字符使用8个比特位来存储。
8比特位和7-bit ASCII:
8比特位其实就是指八个比特位都是有效的可以用来表示字符的位(例如UTF-8),7-bit ASCII编码的字符其实就是经典的我们学习C语言中的那一百多个字符(不知道对不对)。
Lua语言中的字符串是不可变值,不可以像c语言一样直接改变某个字符串中的某个字符。可以使用lua提供的string相关的字符串操作方法获取一个新的字符串。
可以使用长度操作符(#)获取字符串的长度,该操作符返回字符串占用的字节数,在某些编码中,这个值可能与字符串中字符的个数不同。
可以使用连接操作符(..
)来进行字符串的连接(实际返回的是新的字符串,不会修改原有的字符串)。
4.1字符串常量
Lua中可以使用单引号或双引号来声明字符串常量
str1 = '单引号字符串"双引号不用转义"'
str2 = "双引号字符串'单引号不用转义'"
Lua语言中的字符串支持下列C语言风格的转义字符:
转义字符 | 转义 |
\a | 响铃 |
\b | 退格 |
\n | 换行 |
\f | 换页 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ | \ |
" | " |
’ | ’ |
除上面的转义字符外,还可以通过\ddd和\xhh来声明字符。其中\ddd是由最多三位十进制数字组成的序列,这三位数表示的是字符对应的ASCII编码对应的值,不满三位补零。hh为两位十六进制,同样表示的是字符对应的ASCII编码对应的值的十六进制表示。
在Lua5.3版本中新增\u{h…}来声明UTF-8字符,大括号中可以支持任意有效的十六进制。
4.2长字符串/多行字符串
像多行注释一样,可以使用一对双方括号来声明长字符串常量。被方括号括起来的内容可以包括多行,并且其中的转义序列不会被转义。并且如果多行字符串的第一个字符是换行符,那么会忽略该换行符。
同多行注释一样(或者说多行注释其实就像是注释掉一个多行字符串),多行字符串可以在方括号中间添加对应数量的等于号(=)来匹配对应的结束方括号,防止出现字符串中间有两个方括号导致字符串出错。
当代码中需要使用常量文本时,使用长字符串是一个理想的选择。但是,对于非文本的常量我们不应该滥用长字符串。对于像"\r\n"一样的EOF序列在被读取时可能会被归一化为"\n"。作为替代方案可以将这些可能引起歧义的二进制数据ongoing十进制数值或十六进制数值转义序列进行表示,即使用上小节提到的\ddd和\xhh。不过可能导致字符串过长,所以Lua5.2引入了转义序列\z,该转义符会跳过其后所有空白字符,直到遇到的第一个非空白字符。
str = "abcd\z b\z
bbbb"
print(str);--输出abcdbbbbb
4.3强制类型转换
Lua语言在运行时提供了数值和字符串之间的自动转换。针对字符串进行算数操作会尝试将字符串转换为数值。除此之外,在任何需要数值的时候也会进行自动转换,例如作为math.sin参数的时候。
当Lua语言发现需要字符串的地方出现数值时也会将数值转换为字符串,例如使用连接..
操作数值时。
注意使用连接操作时,如果操作对象是数值必须使用空格将其进行分隔,否则会将第一个点识别为小数点。
Lua5.3新引入了整数类型,不过Lua5.3并没有实现强制类型转换和整型的集成。对于整型,算数运算规则是只有在两个操作数都是整型值的时候结果才可能会是整型(部分强制转换为浮点运算)。因此,由于字符串不是整型值,所以有字符串参与的算数运算都会被当做浮点运算处理。
如果需要显示的将字符串转化为数值,可以使用函数tonumber。当改字符串可以表示为有效数字时,那么函数就按照Lua语法扫描器的规则返回对应的整型值或浮点型值,否则返回nil。
该函数默认采用十进制的规则进行转换,也可以给改函数传入第二个参数表示字符串按照多少进制来进行转换,第二个参数可以是2到36(数字0-9+字母a-z)。
print(tonumber(" 10 "))--10
--科学计数法表示
print(tonumber("1.2e2"))--120.0
--十六进制表示
print(tonumber(" 0x1.3p-4"))--0.07421875
--二进制转换
print(tonumber("10010101",2))--149
--十六进制转换
print(tonumber("00ffff",16))--65535
--36进制转换
print(tonumber("zzz",36))--46655
--转换失败
print(tonumber("我不是数字"))--nil
如果需要将数值显示的转换为string可以调用函数tostring。
print(tostring(100)=="100")--true
与算数操作符不同,比较操作符不会对操作数进行强制类型转换,在比较操作符中混用字符和数值会抛出异常。
4.4字符串标准库
Lua语言解释器本身处理字符串的能力是非常有限的,其真正能够处理字符串的能力主要来自于字符串标准库。
正如开头说的,字符串标准库默认处理的是8bit字符,对于ASCII和ISO-8859-1等编码方式是适用的但是对于Unicode编码完全不适用。
str = "Test String,For String Function!测试字符串,测试字符串函数!1234567890!"
--string.len(s)返回字符串s的长度
print(string.len(str))--85
--string.rep(s,n)返回字符串s重复n次的结果
print(string.rep("abcd",5))--abcdabcdabcdabcdabcd
--string.reverse(s)返回字符串s的翻转
print(string.reverse(str))--!0987654321伡锇曟絿宀镐Μ鐥暞鑻垫尲锊镐Μ鐥暞鑻垫!noitcnuF gnirtS roF,gnirtS tseT
--string.lower(s)将字符串s中的大写字母转换为小写字母
print(string.lower(str))--test string,for string function!娴嬭瘯瀛楃涓诧紝娴嬭瘯瀛楃涓插嚱鏁帮紒1234567890!
--string.upper(s)将字符串s中的小写字母转换为大写字母
print(string.upper(str))--TEST STRING,FOR STRING FUNCTION!娴嬭瘯瀛楃涓诧紝娴嬭瘯瀛楃涓插嚱鏁帮紒1234567890!
--string.sub(s,i,j)从字符串提取第i个到第j个字符(第i位和第j位字符都包括,且索引从1开始)
--该函数也支持像Python那样的负数索引-1表示倒数第一个字符,-2表示倒数第二个字符
print(string.sub(str,1,4))--Test
print(string.sub(str,6,-1))--String,For String Function!娴嬭瘯瀛楃涓诧紝娴嬭瘯瀛楃涓插嚱鏁帮紒1234567890!
print(string.sub(str,-11,-1))--1234567890!
--string.char(...)该函数接受可变长参数,可以接受0个或多个整数作为参数,返回对应参数转换为字符后拼接而成的字符串
print(string.char(97,98,99,100,101))--abcde
--string.byte(s,i)该函数返回字符串s中第i个字符的内部数值表示,i默认为1,同样支持负索引
--string.byte(s,i,j)该函数返回字符串s中索引[i,j]之间的全部字符的内部数值表示,同样支持负索引
print(string.byte(str))--84
print(string.byte(str,-2))--48
print(string.byte(str,-11,-2))--49 50 51 52 53 54 55 56 57 48
--[[string.format(s,...)用于进行字符串格式化和将数值输出为字符串。
该函数将格式化字符串s,其中的每一个指示符替换为使用对应格式进行格式化后的对应参数。
格式化字符串中的指示符由一个百分号和一个代表格式化方式的字母组成:%d,%f,%s,%x等等。与c中printf函数类似
在百分号和字母之间可以包含用于控制格式细节的其他选项
例如.4表示取小数点后四位。
其他的格式化可以参考c语言,因为Lua语言是通过调用c语言来完成相关工作的
]]
print(string.format("x=%d,y=%x,z=%.4f",12,200,12.12345))--x=12,y=c8,z=12.1235(说明:末位进1)
print(string.format("str = %s",str))--str = Test String,For String Function!娴嬭瘯瀛楃涓诧紝娴嬭瘯瀛楃涓插嚱鏁帮紒1234567890!
--string.find(s,schild),在字符串s中搜索schild
--找到了返回起始索引和结束索引
--未找到返回nil
print(string.find(str,"Test"))--1 4
print(string.find(str,"Tast"))--nil
--string.gsub(s,matchstr,replacestr)
--将s中匹配到的matchstr替换为replacestr,返回替换完成的字符串和匹配数目
print(string.gsub(str,"Test","TEST"))--TEST String,For String Function!娴嬭瘯瀛楃涓诧紝娴嬭瘯瀛楃涓插嚱鏁帮紒1234567890! 1
--[[
对于字符串标准库中的函数string.xxx(str,...),我们也可以使用str:xxx(...)来进行调用
]]
print(str:gsub("Test","TEST"))--TEST String,For String Function!娴嬭瘯瀛楃涓诧紝娴嬭瘯瀛楃涓插嚱鏁帮紒1234567890! 1
4.5Unicode编码
提供一个参考文献,对于Unicode编码和UTF-8编码讲的比较详细。
彻底弄懂Unicode编码
看完上述文献的UTF-8相关描述可以得知UTF-8编码对于 ASCII是兼容的。
对于Lua语言操作UTF-8格式的字符串时
函数reverse、upper、lower、byte和char是不适用的因为他们其实操作的都是一字节字符。函数format和rep是适用的,format的%c除外,%c是针对一个字节的字符的,rep其实实际上只是重复一段字节,不论什么格式都无所谓。len和sub也可以用,不过返回的是已字节为单位的索引而不是已字符为单位,所以可能我们的逻辑来说不对,但是对于计算机的逻辑来说是没问题的。
(由于本人电脑没有相应的库,所以没有测试以下的函数,所以没有示例代码)
Lua提供了utf8标准库用于操作UTF-8格式的字符串。
utf8.len(s)返回指定字符串中UTF-8字符的个数,并且该函数还会验证字符串,如果字符串中包含无效字节序列那么会返回nil以及第一个无效字节的位置。
utf8.char和utf8.codepoint在UTF-8环境下等价于string.char和string.byte
不过在codepoint(str,i,j)中的i和j其实是字节位置而不是字符位置,所以如果需要使用字符位置作为索引可以使用函数utf8.offset(s,n)来得到第n个字符位置的字符的字节位置。
utf8.codes(s)该函数用于for循环中遍历UTF-8字符串中的每一个字符。
--结果我没法给因为没有运行过
for i,c in utf8.codes("中文其实也是") do
print(i,c)
end
--不过需要解释以下i和c
--其中i为字节索引位置,c为字节索引位置的字符编码值