快速掌握Lua 5.3 —— 字符串库 (1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/VermillionTear/article/details/50696348

Q:对字符串操作的基本函数?

A:

--[[ string.byte(s [, i [, j]])
     依次返回字符串"s"中从"i""j"的字符的ASCII码。
     "i"默认为1"j"默认为"i""i""j"如果为负数,则在字符串"s"中从后往前数。]]
print(string.byte("abc"))                 -->  97
print(string.byte("abc", 2))              -->  98
print(string.byte("abc", 2, 3))           -->  98    99
print(string.byte("abc", -1))             -->  99
--[[ string.char(···)
     与"string.byte()"功能相反,将ASCII码转换为字符。]]
print(string.char(97))                    -->  a
print(string.char(97, 98, 99))            -->  abc

--[[ string.dump(function [, strip])
     返回一个字符串,字符串是函数"function"被转换为二进制流的内容。
     方便函数的序列化,序列化后的函数可以被传递。
     如果"strip"为真,则二进制流中不携带函数的调试信息(局部变量名,行号,等等。)]]
function foo()
    print("Hello World!")
end

s = string.dump(foo)    -- 函数"foo"被转换为二进制流存储在变量"s"中。
f = load(s)    -- 将字符串中的二进制流转换为函数。"f"相当于"foo"的复本。
f()    -- Hello World!  -- 相当于调用了"foo()"。

--[[ string.len(s)
     返回字符串"s"的长度。内嵌的'\0'也会被统计。]]
print(string.len("abc"))    --> 3
print(string.len("a\000b\000c"))    --> 5

--[[ string.lower(s)
     将字符串中所有大写子母转换为小写。
     string.upper(s)
     将字符串中所有小写子母转换为大写。]]
print(string.lower("aBc$e*f"))    --> abc$e*f
print(string.upper("abc$e*f"))    --> ABC$E*F

--[[ string.rep(s, n [, sep])
     返回重复"n"次,以"sep"为分隔符的字符串。"sep"默认为空字符串。]]
print(string.rep("ab", 3))    --> ababab
print(string.rep("ab", 3, "&*"))    --> ab&*ab&*ab

--[[ string.reverse(s)
     返回字符串"s"的倒序排列。]]
print(string.reverse("abc"))    --> cba

--[[ string.sub(s, i [, j])
     返回字符串"s"中从第"i"个字符到第"j"个字符的子串。
     "i"或"j"如果为负数,则在字符串"s"中从后往前数。
     "j"默认为-1,即字符串最后一个字符。]]
print(string.sub("abc", 2))    --> "bc"
print(string.sub("abc", 1, -2))    --> "ab"

--[[ string.format(formatstring, ···)
     功能类似于C语言中的"sprintf()"。"formatstring"是格式化字符串,
     规则遵循C语言中的格式化字符串规则,除了不支持"*, h, L, l, n,"。
     额外支持"q",可以处理转义字符。]]
print(string.format("pi = %.4f", math.pi))     --> pi = 3.1416
d = 5; m = 11; y = 1990
print(string.format("%02d/%02d/%04d", d, m, y))    --> 05/11/1990
tag, title = "h1", "a title"
print(string.format("<%s>%s</%s>", tag, title, tag))    --> <h1>a title</h1>
print(string.format('%q', 'a string with "quotes" and \n new line'))
--[[ result: 第一行末尾的'\'是多行字符串的换行符。
     "a string with \"quotes\" and \
     new line"]]

Q:什么是”magic characters”?

A:在”pattern”中使用,具有特殊含义的字符。

.: (一个点)表示任何一个字符。
+: 匹配一次或多次,最长匹配。
*: 匹配零次或多次,最长匹配。
-: 匹配零次或多次,最短匹配。
?: 匹配零次或一次。
[]: 两个字符组合使用表示一个字符集合。
(): 两个字符组合使用表示一个捕获。
^: 表示从行首开始匹配。
$: 表示匹配到行尾。
%: 转义字符。可以将"magic characters"转义为其字面的含义。

Q:什么是”Character Class”?

A:在”pattern”中使用,可以匹配一个特定字符集合内任何字符的模式项。

x:(这里'x'不能是"magic characters"中的一员)表示字符'x'自身。
%a: 表示任何字母。
%c: 表示任何控制字符。
%d: 表示任何数字。
%g: 表示任何除空格符外的可打印字符。
%l: 表示所有小写字母。
%p: 表示所有标点符号。
%s: 表示空格符。
%u: 表示所有大写字母。
%w: 表示所有字母及数字。
%x: 表示所有十六进制数字符号。
%x: '%'是转义字符,'x'是任意非字母或数字的字符,"%x"表示字符'x'本身。
    '%'是对"magic characters"转义的标准方法。
[set]: 表示一个字符的集合"set"。
       可以使用'-'字符以升序的方式连接两个字符以表示一个范围内的所有字符,
       也可以单独列出需要的字符以组成一个字符的集合。
       例如,"[%w_]"(或"[_%w]")表示所有的字母数字加下划线的字符集合,
       "[0-7]"表示八进制数字的字符集合,
       "[0-7%l%-]"表示八进制数字加小写字母加'-'字符的字符集合。
[^set]: 表示一个字符的集合"set"的补集,其中"set"如上面的解释。

上面从"%a"到第一个"%x"的这些模式项,若将其字母改为大写,均表示其对应的补集。
例如,"%S"表示所有非空格字符的字符。 

Q:什么是最短匹配与最长匹配?

A:根据”pattern”,匹配尽可能多的字符是最长匹配,反之为最短匹配。
以C语言中匹配注释的实例进行说明,实例最直观,

code = "int x; /* x */  int y; /* y */"
-- "string.gsub()"将匹配到的字符串替换为指定字符串。
print(string.gsub(test, "/%*.*%*/", "<COMMENT>"))
--> int x; <COMMENT>
--[[ '*'使用最长匹配,直到找到最后一个可匹配的"*/"时才结束匹配,
     而对于本例,不是我们最终想要的结果。]]
print(string.gsub(test, "/%*.-%*/", "<COMMENT>"))
--> int x; <COMMENT>  int y; <COMMENT>
--[[ '-'使用最短匹配,当找到第一个可匹配的"*/"时就结束匹配,
     对于本例,这才是我们想要的结果。]]

注意,此实例只是说明问题,并不是匹配C语言中注释的最佳方法,见“附加 4”。

Q:什么是捕获?

A:”pattern”可以在内部用()括起一个”sub-pattern”,这些”sub-pattern”被称为捕获物。当匹配成功时,由捕获物匹配到的字符串中的子串被保存起来用于未来的用途。捕获物以它们左括号的次序来编号。例如,对于(a*(.)%w(%s*)),字符串中匹配到a*(.)%w(%s*)的部分保存在第一个捕获物中(因此是编号1);由.匹配到的字符是2号捕获物,匹配到%s*的那部分是3号。

pair = "name = Anna"
--[[ "string.find()"在指定字符串中查找匹配"pattern"的子串,
     返回子串的起始和终止位置。如果"pattern"中指定了捕获,则额外返回捕获物。]]
_, _, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)")
print(key, value)    --> name  Anna  -- 捕获物1号和2号。

date = "17/7/1990"
_, _, d, m, y = string.find(date, "(%d+)/(%d+)/(%d+)")
print(d, m, y)    --> 17  7  1990  -- 捕获物1号、2号和3号。

使用%d(d是1到9之间的任一数字)的形式使用捕获物。 比如需要匹配一个单引号或双引号引起来的句子,最开始的”pattern”可能这么写:'["'].-["']',但是这样对于”it’s all right”这个句子会遇到麻烦,因为双引号中包含单引号,所以此时就需要用到捕获,

s = [[then he said: "it's all right"!]]
--[[ "%1"代表使用第一个捕获物。
     第一个捕获如果匹配的是双引号,那么第一个捕获物就是双引号;
     第一个捕获如果匹配的是单引号,那么第一个捕获物就是单引号。]]
a, b, c, quotedPart = string.find(s, "([\"'])(.-)%1")
print(quotedPart)    --> it's all right
print(c)    --> "  -- 这里可以看出来捕获的是双引号。

附加:

1、Lua中的字符串中的任一字符值都是不能被改变的,字符串库中的函数都不会改变参数中字符串的值,而是返回一个新的字符串。如果想改变字符串变量的值,请对其重新赋值,

s = string.sub(s, i, j)

2、”Character Class”定义了很多常用的”pattern”,

[a-z] <--> %l
[0-9] <--> %d
[^%s] <--> %S
...

所以请尽量使用”Character Class”提供的常用”pattern”,即简便,又不会出错。
3、有一个类似于”Character Class”的匹配模式%bxy, 表示在两个不相同的字符’x’与’y’之间的字符的集合(含’x’与’y’)。匹配从左到右读取字符串,对每次读到一个’x’就”+1”,读到一个’y’就”-1”, 最终结束处的那个’y’是第一个记数到”0”的’y’。

print(string.gsub("a (enclosed (in) parentheses) line", "%b()", ""))
--> a  line    1
print(string.gsub("a (enclosed (in) parentheses) line", "%bed", ""))
--> a (enclos (in) parentheses) line    1

4、匹配C语言注释的实例存在漏洞,

test = [[char s[] = "a /* here";  /* a tricky string */]]
print(string.gsub(test, "/%*.-%*/", "<COMMENT>"))
--> char s[] = "a <COMMENT>

“pattern”是个强大的工具,但是任何强大的东西都要小心使用,对正常的解析器而言,”pattern”不是一个替代品。

猜你喜欢

转载自blog.csdn.net/VermillionTear/article/details/50696348
5.3