-
Notifications
You must be signed in to change notification settings - Fork 8
十二、正则表达式
虽然Lua支持正则匹配且功能齐全,但在Nginx上推荐使用Lua-lua提供的指令。
语法:captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)
配置环境:init_worker_by_lua*,set_by_lua*,rewrite_by_lua*,access_by_lua*,content_ by_lua*,header_filter_by_lua*,body_filter_by_lua*,log_by_lua*,ngx.timer.,balancer_by_ lua,ssl_certificate_by_lua*,ssl_session_fetch_by_lua*,ssl_session_store_by_lua*
含义:使用Perl兼容的正则表达式来匹配subject参数,只返回匹配到的第一个结果。如果匹配失败,则返回nil;如果有异常,则返回nil和一个描述错误信息的err。
示例:
location / {
`content_by_lua_block {`
`local ngx = require "ngx";`
`--匹配多个数字+aaa的正则表达式`
`local m, err = ngx.re.match(ngx.var.uri, "([0-9]+)(aaa)");`
`if m then`
`--匹配成功后输出的信息`
`ngx.say(ngx.var.uri, '---match success---', 'its type: ',type(m))`
`ngx.say(ngx.var.uri, '---m[0]--- ', m[0])`
`ngx.say(ngx.var.uri, '---m[1]--- ', m[1])`
`ngx.say(ngx.var.uri, '---m[2]--- ', m[2])`
`else`
`if err then`
`ngx.log(ngx.ERR, "error: ", err)`
`return`
`end`
`ngx.say("match not found")`
`end`
`}`
}
执行结果如下:
# curl 'http://testnginx.com/test/a123aaa/b456aaa/c'
/test/a123aaa/b456aaa/c---match success---its type: table
/test/a123aaa/b456aaa/c---m[0]---123aaa
/test/a123aaa/b456aaa/c---m[1]---123
/test/a123aaa/b456aaa/c---m[2]---aaa
从执行结果可以看出:
1.ngx.re.match只返回匹配到的第一个结果,所以后面的456aaa并没有被输出。
2.ngx.re.match返回的结果是table类型的。
3.ngx.re.match匹配成功后,m[0] 的值是匹配到的完整数据,而m[1]、m[2] 是被包含在括号内的单个匹配结果。
ngx.re.match只返回第一次匹配成功的数据,如果想获取所有符合正则表达式的数据,可以使用ngx.re.gmatch。
语法:iterator, err = ngx.re.gmatch(subject, regex, options?) 配置环境:init_worker_by_lua*,set_by_lua*,rewrite_by_lua*,access_by_lua*,content_ by_lua*,header_filter_by_lua*,body_filter_by_lua*,log_by_lua*,ngx.timer.,balancer_by_ lua,ssl_certificate_by_lua*,ssl_session_fetch_by_lua*,ssl_session_store_by_lua* 含义:和ngx.re.match功能相似,但返回的是一个Lua迭代器,可以通过迭代的方式获取匹配到的全部数据。
location / {
content_by_lua_block {
local ngx = require "ngx";
--参数i表示忽略大小写
local m_table, err = ngx.re.gmatch(ngx.var.uri, "([0-9]+)(aaa)", "i");
if not m_table then
ngx.log(ngx.ERR, err)
return
end
while true do
local m, err = m_table()
if err then
ngx.log(ngx.ERR, err)
return
end
if not m then
break
end
ngx.say(m[0])
ngx.say(m[1])
end
`}`
}
执行结果如下:
# curl 'http://testnginx.com/test/a123aaa/b456AAA/c'
123aaa
123
456AAA
456
ngx.re.match和ngx.re.gmatch都有一个options参数,用来控制匹配的执行方式,options常用参数说明见表7-1。 表7-1 options常用参数说明
ngx.re.match和ngx.re.gmatch在使用过程中都会生成Lua table,如果只需确认正则表达式是否可以匹配成功,推荐使用如下指令。
语法:from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?) 配置环境:init_worker_by_lua*,set_by_lua*,rewrite_by_lua*,access_by_lua*,content_ by_lua*,header_filter_by_lua*,body_filter_by_lua*,log_by_lua*,ngx.timer.,balancer_by_ lua,ssl_certificate_by_lua*,ssl_session_fetch_by _lua*,ssl_session_store_by_lua* 含义:与ngx.re.match类似,但只返回匹配结果的开始位置索引和结束位置索引。 因为ngx.re.find不会创建table来存储数据,所以性能上比ngx.re.match和ngx.re.gmatch要好很多。此时,如果需要捕获匹配到的数据,可以使用Lua的函数string.sub。
location / {
content_by_lua_block {
local ngx = require "ngx";
local uri = ngx.var.uri
--使用o、j两个参数进行匹配,以提升性能
local find_begin,find_end,err = ngx.re.find(uri, "([0-9]+)(aaa)","oj");
if find_begin then
ngx.say('begin: ',find_begin)
ngx.say('end: ',find_end)
--利用Lua的string.sub函数来获取数据
ngx.say('find it: ' ,string.sub(uri, find_begin,find_end))
return
end
}
}
执行结果如下:
# curl 'http://testnginx.com/test/a123aaa/b456AAAa/c'
begin:8
end:13
find it: 123aaa
ngx.re.match、ngx.re.gmatch和 ngx.re.find 都支持ctx参数,有关ctx参数的说明如下。
1.ctx是Lua table类型的,是可选的第4个参数,但若用到第5个参数nth,那么,此位置需要用nil作为占位符。
2.当ctx有值(键是pos,如pos=1)时,ngx.re.find将从pos位置开始进行匹配(位置的下标从1开始)。
3.无论ctx表中是否有值,ngx.re.find都会在正则表达式匹配成功后,将ctx值设置为所匹配字符串之后的位置;若匹配失败,ctx表将保持原有的状态。
nth是ngx.re.find的第5个参数,是在Lua-Nginx-Module 0.9.3版本之后新增加的参数,它的作用和ngx.re.match中的m[1]、m[2]类似。当nth等于1时,获取的结果等同于ngx.re.match中的m[1],示例如下:
location / {
content_by_lua_block {
`local ngx = require "ngx";`
`local uri = ngx.var.uri`
`--从uri位置为10的地方开始进行匹配,下标默认从1开始,只匹配nth是1的数据,即([0-9]+)的值`
`local ctx = { pos = 10 }`
`local find_begin,find_end,err = ngx.re.find(uri, "([0-9]+)(aaa)","oji",ctx,1);`
`if find_begin then`
`ngx.say('begin: ',find_begin)`
`ngx.say('end: ',find_end)`
`ngx.say('find it: ' ,string.sub(uri, find_begin,find_end))`
`return`
`end`
`}`
}
执行结果如下:
# curl 'http://testnginx.com/test/a123aaa/b456AAAa/c'
begin:10
end:10
find it: 3
因为ctx的位置是10,所以uri前面的“/test/a12”这9个字符被忽略了,匹配到的就只有3aaa,又因为nth为1,所以捕获到的值是3。
Lua API也支持匹配对应数据并对其进行替换的指令。
语法:newstr, n, err = ngx.re.sub(subject, regex, replace, options?) 配置环境:init_worker_by_lua*,set_by_lua*,rewrite_by_lua*,access_by_lua*,content_ by_lua*,header_filter_by_lua*,body_filter_by_lua*,log_by_lua*,ngx.timer.,balancer_by_ lua,ssl_certificate_by_lua*,ssl_session_fetch_by_ lua*,ssl_session_store_by_lua* 含义:若subject中含有参数regex的值,则将之替换为参数replace的值。options为可选参数。替换后的内容将赋值给newstr,n表示匹配到的次数。 示例:
location / {
`content_by_lua_block {`
`local ngx = require "ngx";`
`local uri = ngx.var.uri`
`local n_str, n, err = ngx.re.sub(uri,"([0-9]+)", 'zzzz')`
`if n_str then`
`ngx.say(uri)`
`ngx.say(n_str)`
`ngx.say(n)`
`else`
`ngx.log(ngx.ERR, "error: ", err)`
`return`
`end`
`}`
}
执行结果如下:
# curl 'http://testnginx.com/test188/x2/1231'
/test188/x2/1231
/testzzzz/x2/1231
1
从结果可以看出,只在第一次匹配成功时进行了替换操作,并且只替换了1次,所以n的结果是1。如果要替换匹配到的全部结果可以使用ngx.re.gsub,示例如下:
local n_str, n, err = ngx.re.gsub(uri,"([0-9]+)", 'zzzz')
从执行结果可知,替换了3次:
# curl 'http://testnginx.com/test188/x2/1231'
/test188/x2/1231
/testzzzz/xzzzz/zzzz
3
正则表达式包括\d、\s、\w 等匹配方式,但在Ngx_Lua中使用时,反斜线 \ 会被Lua处理掉,从而导致匹配异常。所以需要对带有 \ 的字符进行转义,转义方式和其他语言有些区别,转义后的格式为\\d、\\s、\\w,因为反斜线会被Nginx和Lua各处理一次,所以\\会先变成\,再变成\。 还可以通过[[]]的方式将正则表达式直接传入匹配指令中,以避免被转义,如下所示:
local find_regex = [[\d+]]
local m = ngx.re.match("xxx,43", find_regex)
ngx.say(m[0]) --输出 43
通常建议使用[[]]的方式。