【问题标题】:Insert quoted and unquoted parts of string in table在表中插入带引号和不带引号的字符串部分
【发布时间】:2015-02-15 09:45:14
【问题描述】:

我一直在研究 saycommand 系统的这一部分,它应该分离字符串的各个部分并将它们放在一个表中,该表被发送到一个函数,该函数在字符串的开头进行查询。例如,这看起来像 !save 1!teleport 0 1!tell 5 "a private message"


我想把这个字符串变成一个表格:

[[1 2 word 2 9 'more words' 1 "and more" "1 2 34"]]

(字符串的每个未引用的部分都有自己的键,而引用的部分被分组为一个键)

1 = 1
2 = 2
3 = word
4 = 2
5 = 9
6 = more words
7 = 1
8 = and more
9 = 1 2 34

我已经尝试使用 Lua 模式执行此操作,但我一直在尝试找出如何捕获字符串中带引号和不带引号的片段。我尝试了很多东西,但没有任何帮助。

我当前的模式尝试如下所示:

a, d = '1 2 word 2 9 "more words" 1 "and more" "1 2 34"" ', {}

     --    previous attempts
    --[[
         This one captures quotes
     a:gsub('(["\'])(.-)%1', function(a, b) table.insert(d, b) end)

         This one captures some values and butchered quotes,
         which might have to do with spaces in the string
     a:gsub('(["%s])(.-)%1', function(a, b) table.insert(d, b) end)

         This one captures every value, but doesn't take care of quotes
     a:gsub('(%w+)', function(a) table.insert(d, a) end)

         This one tries making %s inside of quotes into underscores to
         ignore them there, but it doesn't work
     a = a:gsub('([%w"\']+)', '%1_')
     a:gsub('(["\'_])(.-)%1', function(a, b) table.insert(d, b) end)
     a:gsub('([%w_]+)', function(a) table.insert(d, a) end)

         This one was a wild attempt at cracking it, but no success
     a:gsub('["\']([^"\']-)["\'%s]', function(a) table.insert(d, a) end)
    --]]

    --    This one adds spaces, which would later be trimmed off, to test
    --    whether it helped with the butchered strings, but it doesn't
a = a:gsub('(%w)(%s)(%w)', '%1%2%2%3')
a:gsub('(["\'%s])(.-)%1', function(a, b) table.insert(d, b) end)
for k, v in pairs(d) do
    print(k..' = '..v)
end

简单的命令不需要它,但像!tell 1 2 3 4 5 "a private message sent to five people" 这样更复杂的命令确实需要它,首先检查它是否发送给多个人,然后找出消息是什么。

再往下,我想添加像!give 1 2 3 "component:material_iron:weapontype" "food:calories" 这样的命令,它应该向三个不同的人添加两个项目,这将从这样的系统中受益匪浅。


如果这在 Lua 模式中是不可能的,我会尝试使用 for 循环等,但我真的觉得我错过了一些明显的东西。我是不是想多了?

【问题讨论】:

    标签: string lua lua-patterns


    【解决方案1】:

    您不能使用 Lua 模式处理带引号的字符串。您需要显式解析字符串,如下面的代码所示。

    function split(s)
        local t={}
        local n=0
        local b,e=0,0
        while true do
            b,e=s:find("%s*",e+1)
            b=e+1
            if b>#s then break end
            n=n+1
            if s:sub(b,b)=="'" then
                b,e=s:find(".-'",b+1)
                t[n]=s:sub(b,e-1)
            elseif s:sub(b,b)=='"' then
                b,e=s:find('.-"',b+1)
                t[n]=s:sub(b,e-1)
            else
                b,e=s:find("%S+",b)
                t[n]=s:sub(b,e)
            end
        end
        return t
    end
    
    s=[[1 2 word 2 9 'more words' 1 "and more" "1 2 34"]]
    print(s)
    t=split(s)
    for k,v in ipairs(t) do
        print(k,v)
    end
    

    【讨论】:

      【解决方案2】:

      Lua 字符串模式和正则表达式通常不太适合当您需要进行需要不同嵌套级别或令牌计数平衡(如括号 ( ))的解析时。但是 Lua 还可以使用另一个功能强大的工具来处理该要求:LPeg

      LPeg 语法有点陈旧,需要一些时间来适应,所以我将使用 lpeg re 模块来代替它以使其更易于理解。请记住,您可以用一种形式的语法做任何事情,也可以用另一种形式表达。

      我将首先定义解析格式描述的语法:

      local re = require 're'
      local cmdgrammar =
        [[
          saycmd  <- '!' cmd extra
          cmd     <- %a%w+
          extra   <- (singlequote / doublequote / unquote / .)*
          unquote <- %w+
          singlequote   <- "'" (unquote / %s)* "'"
          doublequote   <- '"' (unquote / %s)* '"'
        ]]
      

      接下来,编译语法并使用它来匹配您的一些测试示例:

      local cmd_parser = re.compile(cmdgrammar)
      local saytest = 
      {
        [[!save 1 2 word 2 9 'more words' 1 "and more" "1 2 34"]],
        [[!tell 5 "a private message"]],
        [[!teleport 0 1]],
        [[!say 'another private message' 42 "foo bar" baz]],
      }
      

      语法中目前没有捕获,因此re.match 返回字符串中它能够匹配的最后一个字符位置 + 1。这意味着成功的解析将返回字符串的完整字符计数 + 1 和因此是您语法的有效实例。

      for _, test in ipairs(saytest) do
        assert(cmd_parser:match(test) == #test + 1)
        end
      

      现在是有趣的部分。一旦你的语法按要求工作,你现在可以添加捕获,自动将你想要的结果提取到一个 lua 表中,而且工作量相对较小。这是最终的语法规范 + 表格捕获:

      local cmdgrammar =
        [[
          saycmd  <- '!' {| {:cmd: cmd :} {:extra: extra :} |}
          cmd     <- %a%w+
          extra   <- {| (singlequote / doublequote / { unquote } / .)* |}
          unquote <- %w+
          singlequote   <- "'" { (unquote / %s)* } "'"
          doublequote   <- '"' { (unquote / %s)* } '"'
        ]]
      

      再次运行测试并转储re.match 结果:

      for i, test in ipairs(saytest) do
        print(i .. ':')
        dump(cmd_parser:match(test))
        end
      

      你应该得到类似的输出:

      lua say.lua
      
      1:
      {
        extra = {
          "1",
          "2",
          "word",
          "2",
          "9",
          "more words",
          "1",
          "and more",
          "1 2 34"
        },
        cmd = "save"
      }
      2:
      {
        extra = {
          "5",
          "a private message"
        },
        cmd = "tell"
      }
      3:
      {
        extra = {
          "0",
          "1"
        },
        cmd = "teleport"
      }
      4:
      {
        extra = {
          "another private message",
          "42",
          "foo bar",
          "baz"
        },
        cmd = "say"
      }
      

      【讨论】:

        猜你喜欢
        • 2011-04-16
        • 2013-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-05
        • 1970-01-01
        • 2017-02-26
        相关资源
        最近更新 更多