在表格中插入带引号和不带引号的字符串部分。

我一直在做一个说话命令系统的一部分,它应该将字符串的部分分离并将它们放入一个表中,然后将该表发送到一个函数中,在字符串的开头查询它。例如,!save 1!teleport 0 1!tell 5“私人消息”

我想将此字符串转换为表格:

[[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"1 2 34“',{}

    -- 先前的尝试
    --[[
        这个捕获引号
    a:gsub('(["\'])(.-)%1',function(a,b)table.insert(d,b)end)

        这个捕获一些值和损坏的引用,由于字符串中可能有空格,因此可能要做一些处理
    a:gsub('(["%s])(.-)%1',function(a,b)table.insert(d,b)end)

        这个捕获每个值,但不处理引用
    a:gsub('(%w +)',function(a)table.insert(d,a)end)

        这个尝试使引号内的%s变成下划线以忽略它们,但它不起作用
    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)

        这是一次突破的疯狂尝试,但没有成功
    a:gsub('["\']([^"\']-)["\'%s]', function(a) table.insert(d, a) end)
    --]]

    -- 这个添加了空格,以后会被修剪掉,以测试是否有助于损坏的字符串,但它不起作用
a = a:gsub'(%w)(%s)(%w)''%1%2%2%3')
a:gsub('(["\'%s])(.-)%1'functionabtable.insertdbend)
对于kv in pairsd)做
    printk..' ='..v)
结束

这对于简单的命令是不需要的,但是像!tell 1 2 3 4 5“发送给五个人的私人消息”这样更复杂的命令确实需要它,首先要检查它是否发送给多个人,然后找出消息是什么。

在更远的路上,我想添加像“!give 1 2 3“component:material_iron:weapontype”“food:calories””这样的命令,它应该向三个不同的人添加两个物品,这将从这样的系统中获益。

如果Lua模式无法实现此目的,我将尝试使用循环等来实现它,但我确实感到自己缺少了一些明显的东西。我是在过度思考吗?

点赞
用户107090
用户107090

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

--定义 split 函数
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
2014-12-17 02:28:48
用户234175
用户234175

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"
}
2014-12-18 04:20:37