LUA 正则表达式 - 模式匹配
按匹配查找:string.find (s,pattern [,init [,plain]])
两个返回值,被捕获内容的起始位置和终止位置,如未找到则返回nil。
参数s:指定字符串,参数pattern:匹配,可选参数init:初始查找位置(如果是负数将从字符串尾开始定位),可选参数plain:无参数时按匹配查找,当参数为true时使用单纯字符串进行匹配。
按位置捕获:string.sub (s,i [,j])
返回以i,j定位的子串内容。
参数s:指定字符串,参数i:初始查找位置(如果是负数将从字符串尾开始定位),可选参数j:无参数时等价j为最大值,如果j比整个字符串大则自动修正,如果j比i小则返回空字符串。
按匹配捕获:string.match (s,pattern [,init])
返回按匹配捕获的子串,如未捕获则返回nil。如果在参数pattern中使用()进行捕获则返回值变为捕获到内容。
参数s:指定字符串,参数pattern:匹配,可选参数init:初始查找位置(如果是负数将从字符串尾开始定位)
按匹配捕获并替换:string.gsub (s,pattern,replace [,n])
两个返回值,成功替换后的新字符串和被替换的次数,如未找到匹配则返回s原始内容。
参数s:指定字符串,参数pattern:匹配,参数replace:替换内容(可以是表或函数),可选参数n:替换次数,无参数则全部替换。
迭代器函数:string.gmatch (s,pattern)
每次迭代返回一个匹配的子串内容,该函数用于逐一遍历字符串中所有捕获到的匹配。
参数s:指定字符串,参数pattern:匹配
模式匹配预置的字符分类
-- 任意字符%a -- 字母%c -- 控制符,如\n%d -- 数字%g -- 除空格外的可打印字符%l -- 小写字母%p -- 标点符号%s -- 空白字符%u -- 大写字母%w -- 字母和数字%x -- 十六进制数字%n -- 第n次被()捕获的内容,n是一个数字,如:%1 %2 %3,而%0则意味着整个匹配而不是捕获%f -- 前置排除模式%b -- 范围匹配,使用两个字符参数确定前后成对的区域,如:%b<> %b{} %b33 %b-+ 遵循最远距离贪婪匹配规则
魔法字符(修饰符)
% -- 转义符
+ -- 重复一次或多次
* -- 重复零次或多次(最远距离贪婪匹配)
- -- 重复零次或多次(最近距离匹配)
? -- 重复零次或一次(表示可有可无)
^ -- 锚定开头
$ -- 锚定结尾[] -- 自定义字符分类,分类间的关系是"或"() -- 捕获,空的捕获可以返回位置信息,如:字符串"Name",模式"()am()"返回2,4。
使用修饰符时一定要注意上下文,如:.*很可能匹配超预期
在结尾使用"-"并没有意义,这样只能匹配到空字符,-后面总是要跟上一些东西来限制扩展范围
当模式匹配字母大写时表示其补集,非字母代表其本身,在模式中%需要转义,如%%。而匹配无意义的修饰符时也需要加转义字符%。
前置排除模式:简单来说%f[char-set]是判断[char-set]后面紧跟的字符是否属于[char-set]而[char-set]之前的字符不属于[char-set],例如:"%f[%a]abc123"对于字符串"Hi+abc123"来说,字符a属于[%a]而前面的+不属于[%a]即模式成立。
()捕获与%n配合可以对[]自定义字符分类中不确定的内容在后续进行进一步的确认。
例如:
s = [[then he said: "it's all right"!]]print (string.match (s, "(["'])(.-)%1")-->" it's all right 输出了两个参数,分别是"和it's all right。
以"或'开头,零个或多个任意字符序列,遇到第一次捕获到的匹配为结尾,使得不确定的字符分类在最后得到了确定的匹配
类似的例子如:长字符串或长注释[==[ ]==]中不确定的=数量
翻译一些例子
"[_%a][_%w]*" -- [下划线或任意字母]和[零个或多个下划线或任意字母或数字]*,匹配LUA中合法的标识符"[+-]?%d+" -- [可有可无的正号或负号]?任意数字序列+,匹配123,-21,+456"^%d" -- 任意数字开头^,匹配整段字符串第一个字符是数字"^[+-]?%d+$" -- 开头^[可有可无正号或负号]?任意数字序列结尾$,匹配纯整型数值
"%[(=*)%[(.-)%]%1%]" -- 转义[捕获1(零个或多个等号)转义[捕获2(零个或多个任意字符)转义]捕获1的内容%1转义],匹配LUA长字符串样式[==[]==]
替换
string.gsub的第三个参数不仅可以是字符串,还可以是函数或者表。
string.gsub配合%n替换
例如:
print (string.gsub ("Hello Lua", "(.)(.)", "%2%1"))--> eHll ouLa 4,将相邻的两个任意字符换位,共替换4次
当替换参数是函数时,会在每一次找到匹配时调用这个函数,并以捕获的内容为参数,以这个函数返回值作为替换内容。
当替换参数是表时,会把第一个捕获内容作为键,去查找表中的值作为替换内容,如果不成功,则当次不做任何替换。
例如:
tb={}
tb.Hello="+"print (string.gsub ("Hello Lua", "%a+", tb))--> + Lua 2, 以匹配到的字符序列为键去获取表tb中对应该键的值,如果是标准字符串则使用这个字符串替换,如果没找到键或者值不是字符串类型则不做改变。
本示例匹配到了2次但只替换1次。
小程序练习
-- 给定一个字符串,找出字符串中所有单词并计算重复的次数,输出前进行排序,优先输出重复最多的单词do
local txt = [[hi hi lua aaa b c c c cc]] -- 指定字符串
local tbl = {} for key in string.gmatch(txt, "%a+") do -- 每次迭代逐个捕获表中单词,存入表tbl
tbl[key] = (tbl[key] or 0) + 1 -- 作为键存表,其值做一次计数自增
end
local words = {} for k, v in pairs(tbl) do -- 把无序表tbl的键作为值按顺序导入表words
words[#words + 1] = k end
table.sort (words, function(v1, v2) return tbl[v1] > tbl[v2] or tbl[v1] == tbl[v2] and v1 < v2 end) -- 排序表words,比较tbl中相关键的值(重复的次数),优先按次数最多排序,如果次数相等则按首字母顺序排
for _,v in ipairs(words) do -- 遍历数组输出
print ("单词 "..v.." 被找到 "..tbl[v].." 次") endend
--[[ 输出结果
单词 c 被找到 3 次
单词 hi 被找到 2 次
单词 aaa 被找到 1 次
单词 b 被找到 1 次
单词 cc 被找到 1 次
单词 lua 被找到 1 次
--]