我是靠谱客的博主 朴素翅膀,这篇文章主要介绍LuaString库捕获和替换(标准库相关),现在分享给大家,希望可以做个参考。

捕获(Captures)

Capture3是这样一种机制:可以使用模式串的一部分匹配目标串的一部分。将你想捕获的模式用圆括号括起来,就指定了一个capture。

在 string.find 使用 captures 的时候,函数会返回捕获的值作为额外的结果。这常被用来将一个目标串拆分成多个:

复制代码
1
2
3
4
pair = "name = Anna" _, _, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)") print(key, value) --> name Anna

‘%a+’ 表示菲空的字母序列;’%s*’ 表示 0 个或多个空白。在上面的例子中,整个模式代表:一个字母序列,后面是任意多个空白,然后是 ‘=’ 再后面是任意多个空白,然后是一个字母序列。两个字母序列都是使用圆括号括起来的子模式,当他们被匹配的时候,他们就会被捕获。当匹配发生的时候,find 函数总是先返回匹配串的索引下标(上面例子中我们存储哑元变量 _ 中),然后返回子模式匹配的捕获部分。下面的例子情况类似:

复制代码
1
2
3
4
date = "17/7/1990" _, _, d, m, y = string.find(date, "(%d+)/(%d+)/(%d+)") print(d, m, y) --> 17 7 1990

我们可以在模式中使用向前引用,’%d’(d 代表 1-9 的数字)表示第 d 个捕获的拷贝。看个例子,假定你想查找一个字符串中单引号或者双引号引起来的子串,你可能使用模式 ‘["’].-["’]’,但是这个模式对处理类似字符串 “it’s all right” 会出问题。为了解决这个问题,可以使用向前引用,使用捕获的第一个引号来表示第二个引号:

复制代码
1
2
3
4
5
s = [[then he said: "it's all right"!]] a, b, c, quotedPart = string.find(s, "(["'])(.-)%1") print(quotedPart) --> it's all right print(c) --> "

第一个捕获是引号字符本身,第二个捕获是引号中间的内容(’.-’ 匹配引号中间的子串)。

捕获值的第三个应用是用在函数 gsub 中。与其他模式一样,gsub 的替换串可以包含 ‘%d’,当替换发生时他被转换为对应的捕获值。(顺便说一下,由于存在这些情况,替换串中的字符 ‘%’ 必须用 “%%” 表示)。下面例子中,对一个字符串中的每一个字母进行复制,并用连字符将复制的字母和原字母连接起来:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
print(string.gsub("hello Lua!", "(%a)", "%1-%1")) --> h-he-el-ll-lo-o L-Lu-ua-a! 下面代码互换相邻的字符: print(string.gsub("hello Lua", "(.)(.)", "%2%1")) --> ehll ouLa 让我们看一个更有用的例子,写一个格式转换器:从命令行获取 LaTeX 风格的字符串,形如: command{some text} 将它们转换为 XML 风格的字符串: <command>some text</command> 对于这种情况,下面的代码可以实现这个功能: s = string.gsub(s, "\(%a+){(.-)}", "<%1>%2</%1>") 比如,如果字符串 s 为: the quote{task} is to em{change} that. 调用 gsub 之后,转换为: the <quote>task</quote> is to change that. 另一个有用的例子是去除字符串首尾的空格: function trim (s) return (string.gsub(s, "^%s*(.-)%s*$", "%1")) end

注意模式串的用法,两个定位符(’^’ 和 ‘$’)保证我们获取的是整个字符串。因为,两个 ‘%s*’ 匹配首尾的所有空格,’.-’ 匹配剩余部分。还有一点需要注意的是 gsub 返回两个值,我们使用额外的圆括号丢弃多余的结果(替换发生的次数)。

最后一个捕获值应用之处可能是功能最强大的。我们可以使用一个函数作为string.gsub 的第三个参数调用 gsub。在这种情况下,string.gsub 每次发现一个匹配的时候就会调用给定的作为参数的函数,捕获值可以作为被调用的这个函数的参数,而这个函数的返回值作为 gsub 的替换串。先看一个简单的例子,下面的代码将一个字符串中全局变量$varname 出现的地方替换为变量 varname 的值:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function expand (s) s = string.gsub(s, "$(%w+)", function (n) return _G[n] end) return s end name = "Lua"; status = "great" print(expand("$name is $status, isn't it?")) --> Lua is great, isn't it? 如果你不能确定给定的变量是否为 string 类型,可以使用 tostring 进行转换: function expand (s) return (string.gsub(s, "$(%w+)", function (n) return tostring(_G[n]) end)) end print(expand("print = $print; a = $a")) --> print = function: 0x8050ce0; a = nil 下面是一个稍微复杂点的例子,使用 loadstring 来计算一段文本内$后面跟着一对方括号内表达式的值: s = "sin(3) = $[math.sin(3)]; 2^5 = $[2^5]" print((string.gsub(s, "$(%b[])", function (x) x = "return " .. string.sub(x, 2, -2) local f = loadstring(x) return f() end))) --> sin(3) = 0.1411200080598672; 2^5 = 32

第一次匹配是 “ [ m a t h . s i n ( 3 ) ] " , 对 应 的 捕 获 为 " [math.sin(3)]",对应的捕获为 " [math.sin(3)]""[math.sin(3)]”,调用 string.sub 去掉首尾的方括号,所以被加载执行的字符串是 “return math.sin(3)”,"$[2^5]" 的匹配情况类似。

我们常常需要使用 string.gsub 遍历字符串,而对返回结果不感兴趣。比如,我们收集一个字符串中所有的单词,然后插入到一个表中:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
words = {} string.gsub(s, "(%a+)", function (w) table.insert(words, w) end) 如果字符串 s 为 "hello hi, again!",上面代码的结果将是: {"hello", "hi", "again"} 使用 string.gfind 函数可以简化上面的代码: words = {} for w in string.gfind(s, "(%a)") do table.insert(words, w) end gfind 函数比较适合用于范性 for 循环。他可以遍历一个字符串内所有匹配模式的子 串。我们可以进一步的简化上面的代码,调用 gfind 函数的时候,如果不显示的指定捕 获,函数将捕获整个匹配模式。所以,上面代码可以简化为: words = {} for w in string.gfind(s, "%a") do table.insert(words, w) end

下面的例子我们使用 URL 编码,URL 编码是 HTTP 协议来用发送 URL 中的参数进行的编码。这种编码将一些特殊字符(比如 ‘=’、’&’、’+’)转换为 “%XX” 形式的编码,其中 XX 是字符的 16 进制表示,然后将空白转换成 ‘+’。比如,将字符串 “a+b = c” 编码为 “a%2Bb+%3D+c”。

最后,将参数名和参数值之间加一个 ‘=’;在 name=value 对之间加一个 “&”。比如字符串:
name = “al”; query = “a+b = c”; q=“yes or no”
被编码为:
name=al&query=a%2Bb+%3D+c&q=yes+or+no
现在,假如我们想讲这 URL 解码并把每个值存储到表中,下标为对应的名字。下面的函数实现了解码功能:

复制代码
1
2
3
4
5
6
7
8
function unescape (s) s = string.gsub(s, "+", " ") s = string.gsub(s, "%%(%x%x)", function (h) return string.char(tonumber(h, 16)) end) return s end

第一个语句将 ‘+’ 转换成空白,第二个 gsub 匹配所有的 ‘%’ 后跟两个数字的 16 进制数,然后调用一个匿名函数,匿名函数将 16 进制数转换成一个数字(tonumber 在 16进制情况下使用的)然后再转化为对应的字符。比如:

复制代码
1
2
print(unescape("a%2Bb+%3D+c")) --> a+b = c

对于 name=value 对,我们使用 gfind 解码,因为 names 和 values 都不能包含 ‘&’ 和 '='我们可以用模式 ‘[^&=]+’ 匹配他们:

复制代码
1
2
3
4
5
6
7
8
9
cgi = {} function decode (s) for name, value in string.gfind(s, "([^&=]+)=([^&=]+)") do name = unescape(name) value = unescape(value) cgi[name] = value end end

调用 gfind 函数匹配所有的 name=value 对,对于每一个 name=value 对,迭代子将其相对应的捕获的值返回给变量 name 和 value。循环体内调用 unescape 函数解码 name 和value 部分,并将其存储到 cgi 表中。

与解码对应的编码也很容易实现。首先,我们写一个 escape 函数,这个函数将所有的特殊字符转换成 ‘%’ 后跟字符对应的 ASCII 码转换成两位的 16 进制数字(不足两位,前面补 0),然后将空白转换为 ‘+’:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function escape (s) s = string.gsub(s, "([&=+%c])", function (c) return string.format("%%%02X", string.byte(c)) end) s = string.gsub(s, " ", "+") return s end 编码函数遍历要被编码的表,构造最终的结果串: function encode (t) local s = "" for k,v in pairs(t) do s = s .. "&" .. escape(k) .. "=" .. escape(v) end return string.sub(s, 2) -- remove first `&' end t = {name = "al", query = "a+b = c", q="yes or no"} print(encode(t)) --> q=yes+or+no&query=a%2Bb+%3D+c&name=al

最后

以上就是朴素翅膀最近收集整理的关于LuaString库捕获和替换(标准库相关)的全部内容,更多相关LuaString库捕获和替换(标准库相关)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(113)

评论列表共有 0 条评论

立即
投稿
返回
顶部