在 OpenResty 中,同时存在两套正则表达式规范:Lua 语言的规范和 Nginx 的规范;即使您对 Lua 语言中的规范非常熟悉,我们仍不建议使用 Lua 中的正则表达式。
- 因为 Lua 中正则表达式的性能并不如 Nginx 中的正则表达式优秀;
- Lua 中的正则表达式并不符合 POSIX 规范,而 Nginx 中实现的是标准的 POSIX 规范,后者明显更具备通用性。
Lua 中的正则表达式与 Nginx 中的正则表达式相比,有 5%-15%的性能损失,而且 Lua 将表达式编译成 Pattern 之后,并不会将 Pattern 缓存,而是每此使用都重新编译一遍,潜在地降低了性能。Nginx 中的正则表达式可以通过参数缓存编译过后的 Pattern ,不会有类似的性能损失。
o 选项参数用于提高性能,指明该参数之后,被编译的 Pattern 将会在 worker 进程中缓存,并且被当前 worker 进程的每次请求所共享。 Pattern 缓存的上限值通过 lua_regex_cache_max_entries 来修改。
Nginx 中的正则表达式包括ngx.re.match、ngx.re.find、ngx.re.gmatch、ngx.re.sub、ngx.re.gsub,这5个函数,函数的作用域都是:
作用域: 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*
1. ngx.re.match
语法: captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)
只有第一次匹配的结果被返回,如果没有匹配,则返回nil;或者匹配过程中出现错误时,也会返回nil,此时错误信息会被保存在err中。
当匹配的字符串找到时,一个Lua table captures会被返回,captures[0]中保存的就是匹配到的字串,captures[1]保存的是用括号括起来的第一个子模式的结果,captures[2]保存的是第二个子模式的结果,依次类似。
参数:options
a | 锚定模式,只从头开始匹配. |
d | DFA模式,或者称最长字符串匹配语义,需要PCRE 6.0+支持. |
D | 允许重复的命名的子模式,该选项需要PCRE 8.12+支持,例如 local m = ngx.re.match(“hello, world”, “(?<named>\w+), (?<named>\w+)”, “D”) — m[“named”] == {“hello”, “world”} |
i | 大小写不敏感模式. |
j | 启用PCRE JIT编译, 需要PCRE 8.21+ 支持,并且必须在编译时加上选项–enable-jit,为了达到最佳性能,该选项总是应该和’o’选项搭配使用. |
J | 启用PCRE Javascript的兼容模式,需要PCRE 8.12+ 支持. |
m | 多行模式. |
o | 一次编译模式,启用worker-process级别的编译正则表达式的缓存. |
s | 单行模式. |
u | UTF-8模式. 该选项需要在编译PCRE库时加上–enable-utf8 选项. |
U | 与”u” 选项类似,但是该项选禁止PCRE对subject字符串UTF-8有效性的检查. |
x | 扩展模式 |
可选参数:ctx
可选参数ctx可以传入一个Lua Table,传入的Lua Table可以是一个空表,也可以是包含pos字段的Lua Table。如果传入的是一个空的Lua Table,那么,ngx.re.match将会从subject字符串的起始位置开始匹配查找,查找到匹配串后,修改pos的值为匹配字符串的下一个位置的值,并将pos的值保存到ctx中,如果匹配失败,那么pos的值保持不变;如果传入的是一个非空的Lua Table,即指定了pos的初值,那么ngx.re.match将会从指定的pos的位置开始进行匹配,如果匹配成功了,修改pos的值为匹配字符串的下一个位置的值,并将pos的值保存到ctx中,如果匹配失败,那么pos的值保持不变。
示例:
local ctx = {pos = 1} local url = "https://www.baidu.com/s?wd=site%3Ahuangxiaobai.com&iq=site%3Abaidu.com&ie=utf-8" local capture, err = ngx.re.match(url, "site([^&]+)", "jo", ctx) print(capture) print(ctx)
输出:
{"0":"site%3Ahuangxiaobai.com","1":"%3Ahuangxiaobai.com"} {"pos":51}
结论 :
1、ngx.re.match有点类似PHP中的preg_match,第一次匹配后 将会停止搜索。 2、任何时刻带上参数"jo" 3、如无匹配或匹配错误,capture返回nil,使用 if not capture 判断是否匹配即可。
2. ngx.re.find
语法: from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)
该方法与ngx.re.match方法基本类似,不同的地方在于ngx.re.find返回的是匹配的字串的起始位置索引和结束位置索引,如果没有匹配成功,那么将会返回两个nil,如果匹配出错,还会返回错误信息到err中。
该方法相比ngx.re.match,不会创建新的Lua字符串,也不会创建新的Lua Table,因此,该方法比ngx.re.match更加高效,因此,在可以使用ngx.re.find的地方应该尽量使用。
参数options 和 参数 ctx ,参考ngx.re.match
参数:nth
可以指定返回第几个子模式串的起始位置和结束位置的索引值,默认值是0,此时将会返回匹配的整个字串;如果nth等于1,那么将返回第一个子模式串的始末位置的索引值;如果nth等于2,那么将返回第二个子模式串的始末位置的索引值,依次类推。如果nth指定的子模式没有匹配成功,那么将会返回两个nil
结论:
1、ngx.re.match有点类似PHP中的preg_match,第一次匹配后 将会停止搜索。 2、任何时刻带上参数"jo" 3、如无匹配或匹配错误,from、to返回nil,使用 if not from 判断是否匹配即可。 4、性能优越于 ngx.re.match
3. ngx.re.gmatch
语法: iterator, err = ngx.re.gmatch(subject, regex, options?)
与ngx.re.match相似,区别在于该方法返回的是一个Lua的迭代器,这样就可以通过迭代器遍历所有匹配的结果。
如果匹配失败,将会返回nil,如果匹配出现错误,那么还会返回错误信息到err中。
示例:
local url = "https://www.baidu.com/s?wd=sit1e%3Ahuangxiaobai.com&iq=sit1e%3Abaidu.com&ie=utf-8" local iterator, err = ngx.re.gmatch(url, "site([^&]+)", "jo") if iterator then while true do local it, err = iterator() if not it then break end print(it) end end
输出:
{"0":"site%3Ahuangxiaobai.com","1":"%3Ahuangxiaobai.com"} {"0":"site%3Abaidu.com","1":"%3Abaidu.com"}
注意:ngx.re.gmatch返回的迭代器只能在一个请求所在的环境中使用,就是说,我们不能把返回的迭代器赋值给持久存在的命名空间(比如一个Lua Packet)中的某一个变量。
结论:
1、ngx.re.match有点类似PHP中的preg_match_all 2、任何时刻带上参数"jo" 3、如无匹配或匹配错误,iterator 返回nil,使用 if not iterator 判断是否匹配即可。 4、ngx.re.gmatch返回的迭代器只能在一个请求所在的环境中使用,就是说,我们不能把返回的迭代器赋值给 持久存在的命名空间
4. ngx.re.sub
语法: newstr, n, err = ngx.re.sub(subject, regex, replace, options?)
该方法主要实现匹配字符串的替换,会用replace替换匹配的字串,replace可以是纯字符串,也可以是使用$0, $1等子模式串的形式,ngx.re.sub返回进行替换后的完整的字符串,同时返回替换的总个数;options选项,与ngx.re.match中的options选项是一样的。
示例:
local url = "https://www.baidu.com/s?wd=site%3Ahuangxiaobai.com&iq=site%3Abaidu.com&ie=utf-8" local newStr, n, err = ngx.re.sub(url, "site([^&]+)", "[$0][$1]", "jo") print(newStr)
输出:
https://www.baidu.com/s?wd=[site%3Ahuangxiaobai.com][%3Ahuangxiaobai.com]& iq=site%3Abaidu.com&ie=utf-8
结论:
1、也只仅匹配一次,第一次匹配后 将会停止搜索。 2、如无匹配,则newstr返回,和subject相同的字符串。 3、$0表示整个匹配的子串,$1表示第一个子模式匹配的字串,以此类推。 4、可以用大括号{}将相应的0,1,2...括起来,以区分一般的数字。 5、如果想在replace字符串中显示$符号,可以用$进行转义(不要用反斜杠\$对美元符号进行转义,这种方法不会 得到期望的结果) 6、如果replace是一个函数,那么函数的参数是一个"match table", 而这个"match table"与ngx.re.match 中的返回值captures是一样的,replace这个函数根据"match table"产生用于替换的字符串。
例如:
local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "${0}00")
例如:
local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "$$")
例如:
local replace = function(m) return "["..m[0].."]["..m[1].."]" end local url = "https://www.baidu.com/s?wd=site%3Ahuangxiaobai.com&iq=site%3Abaidu.com&ie=utf-8" local newStr, n, err = ngx.re.sub(url, "site([^&]+)", replace, "jo") print(newStr)
输出:
https://www.baidu.com/s?wd=[site%3Ahuangxiaobai.com][%3Ahuangxiaobai.com]& iq=site%3Abaidu.com&ie=utf-8
5. ngx.re.gsub
语法: newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)
该方法与ngx.re.sub是类似的,但是该方法进行的是全局替换。
示例:
local replace = function(m) return "["..m[0].."]["..m[1].."]" end local url = "https://www.baidu.com/s?wd=site%3Ahuangxiaobai.com&iq=site%3Abaidu.com&ie=utf-8" local newStr, n, err = ngx.re.gsub(url, "site([^&]+)", replace, "jo") print(newStr)
输出:
https://www.baidu.com/s?wd=[site%3Ahuangxiaobai.com][%3Ahuangxiaobai.com] &iq=[site%3Abaidu.com][%3Abaidu.com]&ie=utf-8
结论:
1、用法同ngx.re.sub,只不过该方法进行的是全局替换
Pingback引用通告: openResty中ngx_lua模块提供的API | 精彩每一天