我可以创建一个 returns 可变数值的 gmatch 模式吗?
Can I create a gmatch pattern that returns a variadic number of values?
我需要在我正在编写的程序中迭代一些字符串对。我没有将字符串对放在一个大的 table-of-table 中,而是将它们全部放在一个字符串中,因为我认为最终结果更容易阅读:
function two_column_data(data)
return data:gmatch('%s*([^%s]+)%s+([^%s]+)%s*\n')
end
for a, b in two_column_data [[
Hello world
Olá hugomg
]] do
print( a .. ", " .. b .. "!")
end
输出是你所期望的:
Hello, world!
Olá, hugomg!
然而,顾名思义,two_column_data
函数仅在恰好有两列数据时才有效。我怎样才能让它适用于任意数量的列?
for x in any_column_data [[
qwe
asd
]] do
print(x)
end
for x,y,z in any_column_data [[
qwe rty uio
asd dfg hjk
]] do
print(x,y,z)
end
如果有必要,我可以使用 lpeg 完成此任务。
function any_column_data(data)
local f = data:gmatch'%S[^\r\n]+'
return
function()
local line = f()
if line then
local row, ctr = line:gsub('%s*(%S+)','%1 ')
return row:match(('(.-) '):rep(ctr))
end
end
end
local function any_column_data( str )
local pos = 0
return function()
local _, to, line = str:find("([^\n]+)\n", pos)
if line then
pos = to
local words = {}
line:gsub("[^%s]+", function( word )
table.insert(words, word)
end)
return table.unpack(words)
end
end
end
外循环 returns 行,内循环 returns 行字数。
s = [[
qwe rty uio
asd dfg hjk
]]
for s in s:gmatch('(.-)\n') do
for s in s:gmatch('%w+') do
io.write(s,' ')
end
io.write('\n')
end
这里是 lpeg re 版本
function re_column_data(subj)
local t, i = re.compile([[
record <- {| ({| [ %t]* field ([ %t]+ field)* |} (%nl / !.))* |}
field <- escaped / nonescaped
nonescaped <- { [^ %t"%nl]+ }
escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"']], { t = '\t' }):match(subj)
return function()
local ret
i, ret = next(t, i)
if i then
return unpack(ret)
end
end
end
它基本上是 CSV 示例的重做,并支持一些不错的用例的引用字段:带空格的值、空值 ("")、多行值等。
for a, b, c in re_column_data([[
Hello world "test
test"
Olá "hug omg"
""]].."\tempty a") do
print( a .. ", " .. b .. "! " .. (c or ''))
end
我需要在我正在编写的程序中迭代一些字符串对。我没有将字符串对放在一个大的 table-of-table 中,而是将它们全部放在一个字符串中,因为我认为最终结果更容易阅读:
function two_column_data(data)
return data:gmatch('%s*([^%s]+)%s+([^%s]+)%s*\n')
end
for a, b in two_column_data [[
Hello world
Olá hugomg
]] do
print( a .. ", " .. b .. "!")
end
输出是你所期望的:
Hello, world!
Olá, hugomg!
然而,顾名思义,two_column_data
函数仅在恰好有两列数据时才有效。我怎样才能让它适用于任意数量的列?
for x in any_column_data [[
qwe
asd
]] do
print(x)
end
for x,y,z in any_column_data [[
qwe rty uio
asd dfg hjk
]] do
print(x,y,z)
end
如果有必要,我可以使用 lpeg 完成此任务。
function any_column_data(data)
local f = data:gmatch'%S[^\r\n]+'
return
function()
local line = f()
if line then
local row, ctr = line:gsub('%s*(%S+)','%1 ')
return row:match(('(.-) '):rep(ctr))
end
end
end
local function any_column_data( str )
local pos = 0
return function()
local _, to, line = str:find("([^\n]+)\n", pos)
if line then
pos = to
local words = {}
line:gsub("[^%s]+", function( word )
table.insert(words, word)
end)
return table.unpack(words)
end
end
end
外循环 returns 行,内循环 returns 行字数。
s = [[
qwe rty uio
asd dfg hjk
]]
for s in s:gmatch('(.-)\n') do
for s in s:gmatch('%w+') do
io.write(s,' ')
end
io.write('\n')
end
这里是 lpeg re 版本
function re_column_data(subj)
local t, i = re.compile([[
record <- {| ({| [ %t]* field ([ %t]+ field)* |} (%nl / !.))* |}
field <- escaped / nonescaped
nonescaped <- { [^ %t"%nl]+ }
escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"']], { t = '\t' }):match(subj)
return function()
local ret
i, ret = next(t, i)
if i then
return unpack(ret)
end
end
end
它基本上是 CSV 示例的重做,并支持一些不错的用例的引用字段:带空格的值、空值 ("")、多行值等。
for a, b, c in re_column_data([[
Hello world "test
test"
Olá "hug omg"
""]].."\tempty a") do
print( a .. ", " .. b .. "! " .. (c or ''))
end