Skip to content

十二、正则表达式

xikder edited this page Dec 14, 2018 · 1 revision

虽然Lua支持正则匹配且功能齐全,但在Nginx上推荐使用Lua-lua提供的指令。

12.1 单一捕获


ngx.re.match

语法: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] 是被包含在括号内的单个匹配结果。

12.2 全部捕获

ngx.re.match只返回第一次匹配成功的数据,如果想获取所有符合正则表达式的数据,可以使用ngx.re.gmatch。


> 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常用参数说明

12.3 更高效的匹配和捕获

ngx.re.match和ngx.re.gmatch在使用过程中都会生成Lua table,如果只需确认正则表达式是否可以匹配成功,推荐使用如下指令。


ngx.re.find

语法: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。

12.4 替换数据

Lua API也支持匹配对应数据并对其进行替换的指令。


ngx.re.sub

语法: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

12.5 转义符号

正则表达式包括\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

通常建议使用[[]]的方式。

Clone this wiki locally