【问题标题】:Case-insensitive matching in LPeg.re (Lua)LPeg.re (Lua) 中不区分大小写的匹配
【发布时间】:2016-10-15 07:28:42
【问题描述】:

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

  1. 匹配以“gv_$/gv$/v$/v_$/x$/xv$/dba_/all_/cdb_”开头的字符串和前缀“SYS.%s*”或“PUBLIC.%” s*" 是可选的
  2. 字符串不应跟随字母数字,即模式不会匹配“XSYS.DBA_OBJECTS”,因为它跟随“X”
  3. 模式不区分大小写

例如,下面的字符串应该匹配模式:

,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 中的“(?=...)”之类的东西

如能提供图案或相关功能,不胜感激。

【问题讨论】:

    标签: lua lpeg


    【解决方案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
    

    请注意,在词法分析器中很难实现不区分大小写,因此每个字母都必须有一个单独的规则——您最终会需要更多这样的规则。

    此语法处理示例中的 cmets,并与空格一起跳过它们,因此“应该返回”之后的匹配项不会出现在输出中。

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-14
        • 2012-03-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多