捕获机制允许根据一个模式从目标字符串中抽出与该模式匹配的内容用于后续用途,可以通过把模式中需要捕获的部分放到一对圆括号内来指定捕获。
对于具有捕获的模式,函数 string.match
会将所有捕获到的值作为单独的结果返回。换句话说,该函数会将字符串切分成多个被捕获的部分:
pair = "name = Anna" key, value = string.match(pair, "(%a+)%s*=%s*(%a+)") print(key, value) --> name Anna点击复制复制失败已复制
模式 "%a+"
表示一个非空的字母序列,模式 "%s*"
表示一个可能为空的空白序列。因此,上例中的这个模式表示一个字母序列,紧跟着空白序列、一个等号、空白序列以及另一个字母序列。模式中的两个字母序列分别被放在圆括号中,因此在匹配时就能捕获到他们。下面是一个类似的示例:
date = "Today is 2/3/2022" d, m, y = string.match(date, "(%d+)/(%d+)/(%d+)") print(d, m, y) --> 2 3 2022
在这个示例中,使用了 3
个捕获,每个捕获对应一个数字序列。
在模式中,形如 "%n"
的分类(其中 n
是一个数字),表示匹配第 n
个捕获的副本。举一个典型的例子,假设想在一个字符串中寻找一个由单引号或双引号括起来的字符串。那么可能会尝试使用模式 "['"].-['"]"
,它表示一个引号后面跟任意内容及另外一个引号。但是,这种模式在处理像 "it's all right"
这样的字符串时会有问题。要解决这个问题,可以捕获第一个引号然后用它来指明第二个引号:
s = [[then he said: "it's all right"!]] q, quotedPart = string.match(s, "([\"'])(.-)%1") print(quotedPart) --> it's all right print(q) --> "
第 1
个捕获是引号本身,第 2
个捕获是引号中内容(与 ".-"
匹配的子串)。
下面是一个类似的示例,用于匹配 Lua
语言中的长字符串的模式:
%[(=*)%[(.-)%]%1%]
它所匹配的内容依次是:一个左方括号、 0
个或多个等号、另一个左方括号、任意内容(即字符串的内容)、一个右方括号、相同数量的等号以及另一个右方括号:
p = "%[(=*)%[(.-)%]%1%]" s = "a = [=[[[ something ]] ]==] ]=]; print(a)" print(string.match(s, p)) --> = [[ something ]] ]==]
第一个捕获是等号序列,第二个捕获是字符串内容。
被捕获对象的第三个用途是在函数 gsub
的代替字符串中。像模式一样,替代字符串同样可以包括像 "%n"
一样的字符分类,当发生替换时会被替换为相应的捕获。特别的, "%0"
意味着整个匹配,并且替换字符串中的百分号必须被转义为 "%%"
。下面这个示例会重复字符串中的每个字母,并且在每个被重复的字母之间插入一个减号:
print((string.gsub("hello lua!", "%a", "%0-%0"))) --> h-he-el-ll-lo-o l-lu-ua-a!点击复制复制失败已复制
下例交换了相邻的字符:
print((string.gsub("hello Lua", "(.)(.)", "%2%1"))) --> ehll ouLa点击复制复制失败已复制
以下是一个更有用的示例,让我们编写一个原始格式转换器,该格式转换器能读取 LATEX
风格的命令,并将他们转换成 XML
风格:
\command{some text} --> <command>some text</command>
如果不允许嵌套的命令,那么以下调用函数 string.gsub
的代码即可完成这项工作:
s = [[the \quote{task} is to \em{change} that.]] s = string.gsub(s, "\\(%a+){(.-)}", "<%1>%2</%1>") print(s) --> the <quote>task</quote> is to <em>change</em> that.点击复制复制失败已复制
另外一个有用的示例是提出字符串两端的空格:
function trim(s) s = string.gsub(s, "^%s(.-)%s*$", "%1") return s end
注意模式中修饰符的合理运用。两个定位标记( ^
和 $
)保证了我们可以获取到整个字符串。由于中间的 ".-"
只会匹配尽可能少的内容,所以两个 "%s*"
便可匹配到首尾两端的空格。