Openresty资料之正则表达式,


Openresty在表ngx.re里提供六个正则表达式相关函数,它们的底层实现是PCRE 库,速度极快,完全可以代替Lua标准库的宇符串匹配函数。

match:单次正则匹配,同时也会捕获子表达式
gmatch:多次正则匹配(以法代器的方式)
find:同match,但返回的是查找到的位置索引
sub:正则替换
gsub:多次正则替换
split:正则切分

◆ 配置指令:

OpenResty提供两个指令用于优化正则表达式的性能。

1.lua_regex_cache_max_entries num

OpenResty会把程序里出现的正则表达式编译后缓存备用(使用“o”选项),这个指令确定了能够缓存的最多数量, 默认是1024 个。如果我们的程序里大量使用了正则表达式,那么就应该在配置文件里用它增大可缓存的数量。

2.lua_regex_match_limit num

设置正则表达式匹配时回溯( backtraking )的最大次数,可以把它设置的小一些以提高运行效率。参数num的默认值是0 ,表示OpenResty将使用PCRE的默认值10000000(一千万)。

◆ 匹配选项:

OpenResty的正则表达式函数都有一个名为options的参数,它是一个字符串,用来定制匹配行为:

a:“锚定”模式,仅从最开始的位置匹配
d:启用DFA模式,确保匹配最长的可能字符串
D:允许重复的命名捕获( duplicate named pattern)
i:忽略大小写,即大小写不敏感
j:启用PCRE-JIT编译,通常与“o”联用
J:兼容JavaScript 的正则表达式语法
m:多行模式
o:正则表达式仅编译一次( compile once),随后缓存
s:单行模式
u:支持UTF-8编码
u:同“u”,但不验证UTF-8编码的正确性
x:启用“扩展”模式,类似Perl的“/x ”

这些参数字符可以联合使用,同时指定多个功能

ngx.re.match(str,[[\w+z]],'ijo')-- 忽略大小写,编译并缓存

实际开发中最常用的参数是“jo”或“ijo”,启用PCRE JIT编译和缓存,加快正则表达式的处理速度。不过对于正则替换( sub 和gsub )最好不要使用“o”选项,因为替换操作的正则表达式是动态产生的,随时变化,缓存没有意义而且会很快达到lua_regex_cache_max_entries的上限。

◆ ngx.re.match

可以进行单次正则匹配:

captures , err= ngx.re.match (subject , regex , options, ctx, res)

函数在字符串subject里使用正则表达式regex查找“第一个”匹配,结果保存在表captures里,captures[0]是匹配的整个字符串, captures[n]是匹配的子表达式。如果未找到匹配或者出错,那么captures就是nil,err 是错误信息。

参数subject和regex是必须的,而后三个options、ctx和res_table都可以省略:

options:设置正则匹配的模式
cts:一个表,目前仅用ctx.pos设置匹配的起始位置,并存储匹配后的位置
res:存储所有匹配的结果,相当于返回值captures,有利于重用内存。

示例:

local str = "abcd-123-456"
local m = ngx.re.match(str,"(.*)123(.*)","jo")
ngx.print(m[2])  -- 输出-456

local ctx = (pos = 7)  -- 设置查找从位置7开始
local m = ngx.re.match(str,"[0-9]",cts)  -- 查找一个数字,传入ctx 参数
assert(m[0]=="2" and ctx.pos == 8)   -- 匹配成功,匹配后的位置是8

local tab_clear = require "table.clear"  -- 使用LuaJIT的table.clear 函数
tab_clear(m)  -- 优化内存使用,重用表m,之前需要先清空
ctx.pos = 1 -- 查找改从位置1 开始,即从头查找
ngx.re.match(str, "(.*)-(.*)$",ctx,m)  -- 结果存放在参数m里,不使用返回值
assert(m and m[2] =="123")  -- 匹配成功,结果与返回值用法相同

◆ ngx.re.gmatch:

可以进行多次正则匹配

iterator, err= ngx.re.gmatch( subject , regex , options)

它返回的是一个迭代器,用户可以反复执行这个法代器,查找出字符串里所有可能的匹配结果。如果没有匹配,那么法代器就是nil 。

local str = "127.0.0.1,123,456,789 "

-- 多次匹配返回迭代器
local iter,err = ngx.re.gmatch(str,[[([0-9\.]+),?]],"ijo")

if not iter then
	return
end

while true do
	local m,err = iter()
	if not m or err then
		break
	end
	print("get:",m[1])
end

◆ ngx.re.find:

正则查找函数ngx.re.find的功能与ngx.re.match基本相同

from, to, err= ngx.re.find(subject, regex, options, ctx, nth)

返回的匹配是两个位置索引,因为不生成结果表所以比ngx.re.match的成本要低,在仅判断是否存在子串的时候更加合适。

-- 检查是否找到
local str = "abcd-123"
local found = ngx.re.find(str,"123","jo")
assert(found)

-- 使用返回的索引位置取子串
local str = "abcd-123"
local from, to = ngx.re.find(str,[[\d+]],"jo")
assert(string.sub(str,from) == "123")

参数nth会令函数返回第nth个子表达式的位置(注意不是第nth个匹配)

-- 返回第二个子表达式
local str = "abcd-123"
local from, to = ngx.re.find(str,[[(\d)(\d+)]],"",nil,2)
assert(string.sub(str,from)=="23")

◆ ngx.re.sub:

可以进行单次正则替换

newstr, n, err= ngx.re.sub(subject ,regex, replace, options)

函数在subject里查找匹配regex的第一个字符串,然后把它替换成参数replace。因为在Lua 里字符串是不可修改的,所以它返回替换后的新字符串newstr和成功替换的次数n。ngx.re.sub只执行一次替换,所以n通常总是1。

参数replace可以是单纯的字符串,也可以是含有前向捕获的正则表达式,$0表示匹配的整个字符串,$1表示第一个子表达式。

local str = "abcd-123"
str  = ngx.re.sub(str,"ab","cd")
assert(str == "cdcd-123")

str = ngx.re.sub([[(\w+)-(\d+)]],"($1)($2)($1)")  -- 结果是“(cdcd)(123)(cdcd)”

注:如果要替换的字符串里需要使用“$飞那么可以用“$$”来表示:

replace 还可以是一个函数,函数的入口参数是匹配表(相当于ngx.re.rnatch的结果),这样就可以在函数里做更复杂的替换操作

local func = function(m)
	return "***"..m[0].."***"
end

str = ($)(123)(cdcd)
str = ngx.re.sub(str, [[\d+]],func)    -- 结果是 ($)(***123***)(cdcd)

◆ ngx.re.gsub:

可以进行多次正则替换

newstr, n, err = ngx.re.gsub(subject, regex, replace, options)

它的参数和返回值的含义与ngx.re.sub完全相同,唯一的区别是会对subject里所有匹配regex的字符串执行替换操作。

local str = "abcd-123"
str = ngx.re.gsub(str,"[a-z]{2}","xyz")  -- 结果是xyzxyz-123

-- 多次正则替换,直接写替换函数
str = ngx.re.gsub(str,[[\w+]],
	function(m) return "("..m[0]..")" end)  -- 结果(xyzxyz)-(123)

◆ 切分:

正则切分函数split是lua-resty-core 库的一部分,能够非常轻松地切分字符串,但它不内置在ngx.re表内,必须显式加载ngx.re模块才能使用,形式是:

local ngx_re_split = reqire("ngx.re").split  -- 显式加载ngx.re模块
res, err = ngx_re_split(subject , regex, options , ctx , max, res)

函数以regex作为“分隔符”,将subject 切分成若干个小字符串,放置在数组res里

local str = "a,b,c,d"
local res = ngx_re_split(str,",")

split 还有四个额外参数,其中的options,ctx,res的含义同ngx.re.match ,而
参数max 会要求函数切分出最多max个结果,即执行max-1次的匹配动作,在己知可能的字串数量的时候可以提前结束匹配,提高效率

local str = "a,b,c,d"
local res = ngx_re_split(str,",",nil,nil,2)  -- 切分的结果是['a','b,c,d']

相关内容

    暂无相关文章