openResty中正则表达式的使用

在 OpenResty 中,同时存在两套正则表达式规范:Lua 语言的规范和 Nginx 的规范;即使您对 Lua 语言中的规范非常熟悉,我们仍不建议使用 Lua 中的正则表达式。

  1. 因为 Lua 中正则表达式的性能并不如 Nginx 中的正则表达式优秀;
  2. 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,只不过该方法进行的是全局替换

openResty中正则表达式的使用》上有1条评论

  1. Pingback引用通告: openResty中ngx_lua模块提供的API | 精彩每一天

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>