文章目录

  • 一、前言
  • 二、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库中有几个与正则相关的函数:findmatchgmatchgsub,本文我将通过一些案例进行测试实验和讲解。

二、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,可以为负索引。
可选布尔值参数plaintrue时,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,会使用repletable进行键值对替换。