在 OpenResty 中,同時(shí)存在兩套正則表達(dá)式規(guī)范:Lua 語(yǔ)言的規(guī)范和 ngx.re.*
的規(guī)范,即使您對(duì) Lua 語(yǔ)言中的規(guī)范非常熟悉,我們?nèi)圆唤ㄗh使用 Lua 中的正則表達(dá)式。一是因?yàn)?Lua 中正則表達(dá)式的性能并不如 ngx.re.*
中的正則表達(dá)式優(yōu)秀;二是 Lua 中的正則表達(dá)式并不符合 POSIX 規(guī)范,而 ngx.re.*
中實(shí)現(xiàn)的是標(biāo)準(zhǔn)的 POSIX 規(guī)范,后者明顯更具備通用性。
Lua 中的正則表達(dá)式與 Nginx 中的正則表達(dá)式相比,有 5% - 15% 的性能損失,而且 Lua 將表達(dá)式編譯成 Pattern 之后,并不會(huì)將 Pattern 緩存,而是每此使用都重新編譯一遍,潛在地降低了性能。ngx.re.*
中的正則表達(dá)式可以通過參數(shù)緩存編譯過后的 Pattern,不會(huì)有類似的性能損失。
ngx.re.*
中的 o
選項(xiàng),指明該參數(shù),被編譯的 Pattern 將會(huì)在工作進(jìn)程中緩存,并且被當(dāng)前工作進(jìn)程的每次請(qǐng)求所共享。Pattern 緩存的上限值通過 lua_regex_cache_max_entries
來修改。
ngx.re.*
中的 j
選項(xiàng),指明該參數(shù),如果使用的 PCRE 庫(kù)支持 JIT,OpenResty 會(huì)在編譯 Pattern 時(shí)啟用 JIT。啟用 JIT 后正則匹配會(huì)有明顯的性能提升。較新的平臺(tái),自帶的 PCRE 庫(kù)均支持 JIT。如果系統(tǒng)自帶的 PCRE 庫(kù)不支持 JIT,出于性能考慮,最好自己編譯一份 libpcre.so,然后在編譯 OpenResty 時(shí)鏈接過去。要想驗(yàn)證當(dāng)前 PCRE 庫(kù)是否支持 JIT,可以這么做
./configure
中指定 --with-debug
選項(xiàng)error_log
指令中指定日志級(jí)別為 debug
pcre JIT compiling result: 1
即使運(yùn)行在不支持 JIT 的 OpenResty 上,加上 j
選項(xiàng)也不會(huì)帶來壞的影響。在 OpenResty 官方的 Lua 庫(kù)中,正則匹配至少都會(huì)帶上 jo
這兩個(gè)選項(xiàng)。
location /test {
content_by_lua_block {
local regex = [[\d+]]
-- 參數(shù) "j" 啟用 JIT 編譯,參數(shù) "o" 是開啟緩存必須的
local m = ngx.re.match("hello, 1234", regex, "jo")
if m then
ngx.say(m[0])
else
ngx.say("not matched!")
end
}
}
測(cè)試結(jié)果如下:
? ~ curl 127.0.0.1/test
1234
另外還可以試試引入 lua-resty-core
中的正則表達(dá)式 API。這么做需要在代碼里加入 require 'resty.core.regex'
。
lua-resty-core
版本的 ngx.re.*
,是通過 FFI 而非 Lua/C API 來跟 OpenResty C 代碼交互的。某些情況下,會(huì)帶來明顯的性能提升。
Lua 中正則表達(dá)式語(yǔ)法上最大的區(qū)別,Lua 使用 '%' 來進(jìn)行轉(zhuǎn)義,而其他語(yǔ)言的正則表達(dá)式使用 '\' 符號(hào)來進(jìn)行轉(zhuǎn)義。其次,Lua 中并不使用 '?' 來表示非貪婪匹配,而是定義了不同的字符來表示是否是貪婪匹配。定義如下:
符號(hào) | 匹配次數(shù) | 匹配模式 |
---|---|---|
+ | 匹配前一字符 1 次或多次 | 非貪婪 |
* | 匹配前一字符 0 次或多次 | 貪婪 |
- | 匹配前一字符 0 次或多次 | 非貪婪 |
? | 匹配前一字符 0 次或1次 | 僅用于此,不用于標(biāo)識(shí)是否貪婪 |
符號(hào) | 匹配模式 |
---|---|
. | 任意字符 |
%a | 字母 |
%c | 控制字符 |
%d | 數(shù)字 |
%l | 小寫字母 |
%p | 標(biāo)點(diǎn)字符 |
%s | 空白符 |
%u | 大寫字母 |
%w | 字母和數(shù)字 |
%x | 十六進(jìn)制數(shù)字 |
%z | 代表 0 的字符 |
local s = "hello world"
local i, j = string.find(s, "hello")
print(i, j) --> 1 5
local s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do
print(w)
end
-- output :
-- hello
-- world
-- from
-- Lua
local a = "Lua is cute"
local b = string.gsub(a, "cute", "great")
print(a) --> Lua is cute
print(b) --> Lua is great
print(string.gsub("a (enclosed (in) parentheses) line", "%b()", ""))
-- output: a line 1