文章目录
- 一、前言
- 二、lua在线测试
- 三、lua正则表达式元字符
- 四、string.find(s, pattern[, init[, plain]])
- 1、案例1:pattern明确搜索
- 2、案例2:pattern分组正则搜索(一个分组)
- 3、案例3:pattern分组正则搜索(多个分组)
- 4、案例4:init参数,从指定位置开始匹配
- 5、案例5:plain参数为true,pattern作为普通字符串匹配
- 五、string.match(s, pattern[, init])
- 1、案例1:匹配版本号
- 2、案例2:匹配日期
- 六、string.gmatch(s, pattern)
- 1、案例1:迭代匹配日期(不带分组)
- 2、案例2:迭代匹配key/value键值对(带分组)
- 七、string.gsub(s, pattern, repl[, n])
- 1、案例1:repl为常规字符串,实现字符串简单替换
- 2、案例2:repl参数为表达式,通过%n匹配子串(不带分组)
- 3、案例3:repl参数为表达式,通过%n匹配子串(带分组)
- 4、案例4:repl参数为函数,通过函数处理替换
- 5、案例5:reple参数为一个table,实现键值对替换
一、前言
有时候需要在lua
中做一些字符串匹配,使用正则表达式可以很方便地进行字符串匹配,string
库中有几个与正则相关的函数:find
、match
、gmatch
、gsub
,本文我将通过一些案例进行测试实验和讲解。
二、lua在线测试
为了方便大家进行测试,可以使用这个lua
代码在线测试网站进行测试:
地址:http://www.dooccn.com/lua/
三、lua正则表达式元字符
正则表达式由元字符按照规则(语法)组成,它们和一般字符按规则构成了lua
的正则表达式。
元字符 | 描述 | 备注 |
%a | 匹配字母,无论大小写 | |
%b | 匹配对称字符,一般写法为"%bxy",x为开始匹配字符,y为结束匹配字符,xy可随意指定 | 例如:"%b<>"为匹配包括<>在内的所有字符 |
%c | 匹配控制字符 | 例如:换行符\n、制表符\t等 |
%d | 匹配数字0-9 | |
%l | 匹配小写字母a-z | |
%p | 匹配标点符号 | |
%s | 匹配空白符号 | |
%u | 匹配大写字母A-Z | |
%w | 匹配字母和数字 | |
%x | 匹配十六进制数字 | |
%z | 匹配代表0的字符 |
以上字符类除了%b
以外的大写形式表示取反,也就是取小写形式匹配集合的补集
,例如:%A
为匹配除字母外的其他字符,%D
为匹配除数字外的其他字符。
元字符 | 描述 | 备注 |
. | 匹配任意字符 | |
% | 特殊字符的转义字符 | 例如:"%.“为匹配点号,”%%“为匹配百分比符号,跟”"用来转义引号类似 |
() | 匹配与返回括号内的内容 | |
[] | 自定义匹配字符集 | 例如:"[a-z0-9,%]"匹配a-z、0-9、逗号以及百分比号 |
+ | 匹配前一字符1次或多次 | |
* | 匹配前一字符0次或多次 | 最长匹配 |
- | 匹配前一字符0次或多次 | 最短匹配 |
? | 匹配前一字符0次或1次 | |
^ | 匹配字符串开头 | 例如:"^%d+"为以数字开头的匹配 |
$ | 匹配字符串结尾 | 例如:"%d+$"为以数字结尾的匹配 |
四、string.find(s, pattern[, init[, plain]])
在字符串s
中匹配pattern
,如果匹配成功返回第一个匹配到的子串的起始索引和结束索引,如果pattern
中有分组,分组匹配的内容也会接着两个索引值之后返回。如果匹配失败返回nil
。
可选数值参数init
表示从s
中的哪个索引位置开始匹配,缺省值是1
,可以为负索引。
可选布尔值参数plain
为true
时,pattern
作为普通字符串匹配,所有正则中的元字符都只被作为普通字符解析。(这个参数并不是匹配字符串的结束索引)
1、案例1:pattern明确搜索
local index_start, index_end = string.find('hello lua', 'lua')
print(index_start, index_end)
输出结果:
7 9
讲解:
这里没有使用正则表达式,明确搜索lua
关键字,返回了lua
关键字在整个字符串的起始索引和结束索引,注意字符串的起始位置索引是从1
开始的。
2、案例2:pattern分组正则搜索(一个分组)
local index_start, index_end, group = string.find('hello lua', 'hello%s*(%w*)')
print(index_start, index_end, group)
输出结果:
1 9 lua
讲解:%s
表示匹配空白符[ \r\n\t\v\f]
,%s*
表示匹配空白符0
次或多次;%w
表示字母数字[a-zA-Z0-9]
,%w*
表示字母数字0
次或多次;
有分组的情况下,分组匹配的内容会接着两个索引值之后返回。
3、案例3:pattern分组正则搜索(多个分组)
local index_start, index_end, group1, group2 = string.find('hello lua script', 'hello%s*(%w*)%s*(%w*)')
print(index_start, index_end, group1, group2)
输出结果
1 16 lua script
讲解:
有分组的情况下,分组匹配的内容会接着两个索引值之后返回,多个分组会依次返回。
4、案例4:init参数,从指定位置开始匹配
local index_start, index_end = string.find('hello lua, study lua', 'lua', 10)
print(index_start, index_end)
输出结果:
18 20
讲解:
第三个参数是可选数值参数init
,表示从s
中的哪个索引位置开始匹配,缺省值是1
,可以为负索引。
5、案例5:plain参数为true,pattern作为普通字符串匹配
local index_start, index_end = string.find('hello %s', '%s', 1, true)
print(index_start, index_end)
输出结果:
7 8
讲解:
第四个参数是可选布尔值参数plain
,缺省值是false
,当它为true
时,pattern
作为普通字符串匹配,所以上文中的%s
并不表示空白符,而是表示%s
本身。
五、string.match(s, pattern[, init])
在字符串s
中匹配pattern
,如果匹配失败返回nil
。否则,当pattern
中没有分组时,返回第一个匹配到的子串;当pattern
中有分组时,返回第一个匹配到子串的分组,多个分组就返回多个。可选参数init
表示匹配字符串的起始索引,缺省为1
,可以为负索引。
1、案例1:匹配版本号
local versionStr = "1.11.0.0"
local m = string.match(versionStr, "^%d+%.%d+%.%d+%.%d+$")
if m then
print(versionStr)
else
print("version format error")
end
输出结果:
1.11.0.0
讲解:^
表示匹配字符串开头;%d
表示匹配数字[0-9]
,%d+
匹配数字[0-9]
一次或多次;%.
表示匹配.
号;$
表示匹配字符串结尾;
2、案例2:匹配日期
local y, m, d = string.match('2020-11-18', '^(%d+)-(%d+)-(%d+)$')
if y and m and d then
print(string.format('year: %d, month: %d, day: %d', y, m, d))
else
print('datetime format error')
end
输出结果:
year: 2020, month: 11, day: 18
讲解:()
,表示分组,有分组的情况下,string.match
返回多个分组。
六、string.gmatch(s, pattern)
返回一个迭代器。每当迭代器调用时,返回下一个匹配到的子串,如果pattern
中有分组,返回的是子串对应的分组。gmatch
也可以用find
和循环来实现。
1、案例1:迭代匹配日期(不带分组)
for s in string.gmatch('2020-11-18', '%d+') do
print(s)
end
输出结果:
2020
11
18
讲解:string.gmatch
返回一个迭代器,每当迭代器调用时,返回下一个匹配到的子串。
2、案例2:迭代匹配key/value键值对(带分组)
for k, v in string.gmatch('a=520, b=1314', '(%w+)=(%w+)') do
print(k, v)
end
输出结果:
a 520
b 1314
讲解:pattern
中有分组,返回的是子串对应的分组。
七、string.gsub(s, pattern, repl[, n])
替换字符串。把字符串中用模式pattern
匹配到的所有子串替换为repl
指代的子串,返回替换后的字符串和替换的次数。可选数值参数n
表示最多可替换的次数。
参数repl
可以是正则表达式,也可以是函数。当repl
是函数时,函数的参数是模式pattern
捕获的子串,和match
类似,有分组返回分组,无分组返回整个子串。函数最后返回一个字符串。如果repl
是正则表达式,可以用分组序号引用匹配到的分组。
1、案例1:repl为常规字符串,实现字符串简单替换
local targetStr = string.gsub('hello lua', 'lua', 'unity')
print(targetStr)
输出结果:
hello unity
讲解:
参数repl
是一个常规字符串,成功匹配的子串会被repl
直接替换。
再测试个例子:
local targetStr = string.gsub('my age is 30', '%d+', '18')
print(targetStr)
输出结果:
my age is 18
2、案例2:repl参数为表达式,通过%n匹配子串(不带分组)
local targetStr = string.gsub('hello lua 520', '%d+', '%0 ' .. 1314)
print(targetStr)
输出结果
hello lua 520 1314
讲解:%0
表示匹配的子串,即520
3、案例3:repl参数为表达式,通过%n匹配子串(带分组)
local targetStr = string.gsub('hello lua 520 1314', '(%d+)%s+(%d+)', '%1 happy %2')
print(targetStr)
输出结果:
hello lua 520 happy 1314
讲解:%1
表示匹配的第一个分组子串,%2
表示匹配的第二个分组子串。
4、案例4:repl参数为函数,通过函数处理替换
local targetStr = string.gsub('hello lua 518', '%d+', function(s)
return tonumber(s) + 2
end)
print(targetStr)
输出结果:
hello lua 520
讲解:repl
是函数时,函数的参数是模式pattern
捕获的子串,和match
类似。
5、案例5:reple参数为一个table,实现键值对替换
local t = { name = "lua", version = "5.1" }
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
print(x)
输出结果:
lua-5.1.tar.gz
讲解:%$
表示匹配$
本身,%$(%w+)
表示以$
为开头的字符串,比如$name
和$version
,会使用reple
的table
进行键值对替换。