LPeg.re (Lua) 中不区分大小写的匹配
Case-insensitive matching in LPeg.re (Lua)
我是 Lua 的 "LPeg" 和 "re" 模块的新手,目前我想根据以下规则编写一个模式:
- 匹配以"gv_$/gv$/v$/v_$/x$/xv$/dba_/all_/cdb_"开头的字符串,前缀"SYS.%s*"或"PUBLIC.%s*"可选
- 字符串不应跟在字母数字之后,即模式不会匹配 "XSYS.DBA_OBJECTS",因为它跟在 "X"
之后
- 模式不区分大小写
例如,下面的字符串应该匹配模式:
,sys.dba_objects, --should return "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot) --should return "dba_hist_snapshot"
目前我的模式如下,只能匹配大写的非字母数字+字符串:
p=re.compile[[
pattern <- %W {owner* name}
owner <- 'SYS.'/ 'PUBLIC.'
name <- {prefix %a%a (%w/"_"/"$"/"#")+}
prefix <- "GV_$"/"GV$"/"V_$"/"V$"/"DBA_"/"ALL_"/"CDB_"
]]
print(p:match(",SYS.DBA_OBJECTS"))
我的问题是:
- 如何实现不区分大小写的匹配?有一些关于解决方案的主题,但我太新了,无法理解
- 如何精确地return只匹配匹配的字符串,而不是还要加上%W? Java
中的“(?=...)”
如能提供模式或相关功能,不胜感激。
您可以尝试调整此语法
local re = require're'
local p = re.compile[[
pattern <- ((s? { <name> }) / s / .)* !.
name <- (<owner> s? '.' s?)? <prefix> <ident>
owner <- (S Y S) / (P U B L I C)
prefix <- (G V '_'? '$') / (V '_'? '$') / (D B A '_') / (C D B '_')
ident <- [_$#%w]+
s <- (<comment> / %s)+
comment <- '--' (!%nl .)*
A <- [aA]
B <- [bB]
C <- [cC]
D <- [dD]
G <- [gG]
I <- [iI]
L <- [lL]
P <- [pP]
S <- [sS]
U <- [uU]
V <- [vV]
Y <- [yY]
]]
local m = { p:match[[
,sys.dba_objects, --should return "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot) --should return "dba_hist_snapshot"
]] }
print(unpack(m))
。 . .打印匹配 table m
:
sys.dba_objects SyS.Dba_OBJECTS cdb_objects dba_hist_snapshot
请注意,词法分析器很难实现不区分大小写,因此每个字母都必须有一个单独的规则——您最终会需要更多规则。
此语法处理示例中的注释并跳过它们和空格,因此 "should return" 之后的匹配项不会出现在输出中。
您可以 fiddle 使用 prefix
和 ident
规则来指定对象名称中的附加前缀和允许的字符。
注意:!.
表示文件结束。 !%nl
表示 "not end-of-line"。 ! p
和 & p
正在构建非消耗模式,即当前输入指针在匹配时不会递增(仅测试输入)。
注意 2:print
-ing 与 unpack
是一个粗俗的 hack。
注3:这里有一个tracable LPeg re可以用来调试语法的。将 true
传递给 re.compile
的第 3 个参数,以获取对每个访问的规则和位置执行 test/match/skip 操作的执行跟踪。
最后我得到了一个解决方案,但不是很优雅,那就是在re.compile, re.find, re.match and re.gsub
函数中添加一个额外的参数case_insensitive
。当参数值为true
时,调用case_insensitive_pattern
重写模式:
...
local fmt="[%s%s]"
local function case_insensitive_pattern(quote,pattern)
-- find an optional '%' (group 1) followed by any character (group 2)
local stack={}
local is_letter=nil
local p = pattern:gsub("(%%?)(.)",
function(percent, letter)
if percent ~= "" or not letter:match("%a") then
-- if the '%' matched, or `letter` is not a letter, return "as is"
if is_letter==false then
stack[#stack]=stack[#stack]..percent .. letter
else
stack[#stack+1]=percent .. letter
is_letter=false
end
else
if is_letter==false then
stack[#stack]=quote..stack[#stack]..quote
is_letter=true
end
-- else, return a case-insensitive character class of the matched letter
stack[#stack+1]=fmt:format(letter:lower(), letter:upper())
end
return ""
end)
if is_letter==false then
stack[#stack]=quote..stack[#stack]..quote
end
if #stack<2 then return stack[1] or (quote..pattern..quote) end
return '('..table.concat(stack,' ')..')'
end
local function compile (p, defs, case_insensitive)
if mm.type(p) == "pattern" then return p end -- already compiled
if case_insensitive==true then
p=p:gsub([[(['"'])([^\n]-)(%1)]],case_insensitive_pattern):gsub("%(%s*%((.-)%)%s*%)","(%1)")
end
local cp = pattern:match(p, 1, defs)
if not cp then error("incorrect pattern", 3) end
return cp
end
...
我是 Lua 的 "LPeg" 和 "re" 模块的新手,目前我想根据以下规则编写一个模式:
- 匹配以"gv_$/gv$/v$/v_$/x$/xv$/dba_/all_/cdb_"开头的字符串,前缀"SYS.%s*"或"PUBLIC.%s*"可选
- 字符串不应跟在字母数字之后,即模式不会匹配 "XSYS.DBA_OBJECTS",因为它跟在 "X" 之后
- 模式不区分大小写
例如,下面的字符串应该匹配模式:
,sys.dba_objects, --should return "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot) --should return "dba_hist_snapshot"
目前我的模式如下,只能匹配大写的非字母数字+字符串:
p=re.compile[[
pattern <- %W {owner* name}
owner <- 'SYS.'/ 'PUBLIC.'
name <- {prefix %a%a (%w/"_"/"$"/"#")+}
prefix <- "GV_$"/"GV$"/"V_$"/"V$"/"DBA_"/"ALL_"/"CDB_"
]]
print(p:match(",SYS.DBA_OBJECTS"))
我的问题是:
- 如何实现不区分大小写的匹配?有一些关于解决方案的主题,但我太新了,无法理解
- 如何精确地return只匹配匹配的字符串,而不是还要加上%W? Java 中的“(?=...)”
如能提供模式或相关功能,不胜感激。
您可以尝试调整此语法
local re = require're'
local p = re.compile[[
pattern <- ((s? { <name> }) / s / .)* !.
name <- (<owner> s? '.' s?)? <prefix> <ident>
owner <- (S Y S) / (P U B L I C)
prefix <- (G V '_'? '$') / (V '_'? '$') / (D B A '_') / (C D B '_')
ident <- [_$#%w]+
s <- (<comment> / %s)+
comment <- '--' (!%nl .)*
A <- [aA]
B <- [bB]
C <- [cC]
D <- [dD]
G <- [gG]
I <- [iI]
L <- [lL]
P <- [pP]
S <- [sS]
U <- [uU]
V <- [vV]
Y <- [yY]
]]
local m = { p:match[[
,sys.dba_objects, --should return "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot) --should return "dba_hist_snapshot"
]] }
print(unpack(m))
。 . .打印匹配 table m
:
sys.dba_objects SyS.Dba_OBJECTS cdb_objects dba_hist_snapshot
请注意,词法分析器很难实现不区分大小写,因此每个字母都必须有一个单独的规则——您最终会需要更多规则。
此语法处理示例中的注释并跳过它们和空格,因此 "should return" 之后的匹配项不会出现在输出中。
您可以 fiddle 使用 prefix
和 ident
规则来指定对象名称中的附加前缀和允许的字符。
注意:!.
表示文件结束。 !%nl
表示 "not end-of-line"。 ! p
和 & p
正在构建非消耗模式,即当前输入指针在匹配时不会递增(仅测试输入)。
注意 2:print
-ing 与 unpack
是一个粗俗的 hack。
注3:这里有一个tracable LPeg re可以用来调试语法的。将 true
传递给 re.compile
的第 3 个参数,以获取对每个访问的规则和位置执行 test/match/skip 操作的执行跟踪。
最后我得到了一个解决方案,但不是很优雅,那就是在re.compile, re.find, re.match and re.gsub
函数中添加一个额外的参数case_insensitive
。当参数值为true
时,调用case_insensitive_pattern
重写模式:
...
local fmt="[%s%s]"
local function case_insensitive_pattern(quote,pattern)
-- find an optional '%' (group 1) followed by any character (group 2)
local stack={}
local is_letter=nil
local p = pattern:gsub("(%%?)(.)",
function(percent, letter)
if percent ~= "" or not letter:match("%a") then
-- if the '%' matched, or `letter` is not a letter, return "as is"
if is_letter==false then
stack[#stack]=stack[#stack]..percent .. letter
else
stack[#stack+1]=percent .. letter
is_letter=false
end
else
if is_letter==false then
stack[#stack]=quote..stack[#stack]..quote
is_letter=true
end
-- else, return a case-insensitive character class of the matched letter
stack[#stack+1]=fmt:format(letter:lower(), letter:upper())
end
return ""
end)
if is_letter==false then
stack[#stack]=quote..stack[#stack]..quote
end
if #stack<2 then return stack[1] or (quote..pattern..quote) end
return '('..table.concat(stack,' ')..')'
end
local function compile (p, defs, case_insensitive)
if mm.type(p) == "pattern" then return p end -- already compiled
if case_insensitive==true then
p=p:gsub([[(['"'])([^\n]-)(%1)]],case_insensitive_pattern):gsub("%(%s*%((.-)%)%s*%)","(%1)")
end
local cp = pattern:match(p, 1, defs)
if not cp then error("incorrect pattern", 3) end
return cp
end
...