我一直在研究一个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循环等,但我真的觉得我错过了一些明显的东西。我是否想过这个?
答案 0 :(得分:3)
您无法使用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
答案 1 :(得分:1)
( )
)的解析时,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"
}