LPeg.re(Lua)中不区分大小写的匹配

时间:2016-06-14 13:57:37

标签: lua lpeg

我是Lua的“LPeg”和“re”模块的新手,目前我想根据以下规则编写模式:

  1. 匹配以“gv _ $ / gv $ / v $ / v _ $ / x $ / xv $ / dba_ / all_ / cdb_”开头的字符串,以及前缀“SYS。%s *”或“PUBLIC。%” s *“是可选的
  2. 字符串不应该遵循字母数字,即模式与“XSYS.DBA_OBJECTS”不匹配,因为它跟随“X”
  3. 该模式不区分大小写
  4. 例如,下面的字符串应与模式匹配:

    ,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")) 
    

    我的问题是:

    1. 如何实现不区分大小写的匹配?有一些关于解决方案的主题,但我太新了,无法理解
    2. 如何仅返回匹配的字符串,而不是还要加上%W?像Java中的“(?= ...)”
    3. 非常感谢您提供模式或相关功能。

2 个答案:

答案 0 :(得分:1)

您可以尝试调整此语法

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))

。 。 。打印匹配表m

sys.dba_objects SyS.Dba_OBJECTS cdb_objects     dba_hist_snapshot

请注意,在词法分析器中很难实现不区分大小写,所以每个字母必须有一个单独的规则 - 最终你需要更多这些规则。

这个语法正在处理你的样本中的注释,并将它们与空白一起跳过,所以在&#34;之后的匹配应该返回&#34;在输出中不存在。

您可以使用prefixident规则来指定对象名称中的其他前缀和允许的字符。

注意:!.表示文件结束。 !%nl表示&#34;不是行尾&#34;。 ! p& p构造非消费模式,即当前输入指针在匹配时不递增(仅测试输入)。

注意2:print - 使用unpack是一个严重的黑客攻击。

注3:这是一个tracable LPeg re,可用于调试语法。通过true获取re.compile的第3个参数,以获取每个规则和访问位置的测试/匹配/跳过操作的执行跟踪。

答案 1 :(得分:1)

最后我得到了一个解决方案,但不是那么优雅,即在case_insensitive函数中添加一个额外的参数re.compile, re.find, re.match and re.gsub。当参数值为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
...