如何将表格转储到控制台?
Posted
技术标签:
【中文标题】如何将表格转储到控制台?【英文标题】:How to dump a table to console? 【发布时间】:2012-02-28 09:31:44 【问题描述】:我无法显示包含嵌套表(n-deep)的表的内容。我想通过print
语句或快速而肮脏的东西将它转储到标准输出或控制台,但我不知道如何。我正在寻找使用 gdb 打印 NSDictionary
时得到的粗略等价物。
【问题讨论】:
【参考方案1】:我发现这个很有用。因为如果递归它也可以打印嵌套表。它没有在输出中给出最漂亮的格式,但是对于这样一个简单的函数来说,调试起来很难。
function dump(o)
if type(o) == 'table' then
local s = ' '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. ' '
else
return tostring(o)
end
end
例如
local people =
name = "Fred",
address = "16 Long Street",
phone = "123456"
,
name = "Wilma",
address = "16 Long Street",
phone = "123456"
,
name = "Barney",
address = "17 Long Street",
phone = "123457"
print("People:", dump(people))
产生以下输出:
人: [1] = ["address"] = 16 Long Street,["phone"] = 123456,["name"] = Fred, ,[2] = ["address"] = 16 Long 街道,[“电话”] = 123456,[“姓名”] = 威尔玛,,[3] = [“地址”] = 17 长街,["电话"] = 123457,["姓名"] = 巴尼, ,
【讨论】:
非常适合分享不需要外部库的内容。 在非常大的桌子上,您的函数会引发 *** 错误 这不是我写的。我在某处找到它并对其进行了修改以生成可以打印的字符串。很想感谢原作者。 @Herrgot - 那将是一张大得离谱的桌子和大量的嵌套。为简单起见,您无法击败它。它适合大多数快速调试用例。 它可能不是一个非常嵌套的表,而是一个在某处有自引用的表,无限循环,它会遇到 ***。【参考方案2】:我知道这个问题已经被标记为已回答,但让我在这里插入我自己的库。它叫做inspect.lua,你可以在这里找到它:
https://github.com/kikito/inspect.lua
这只是您可以从任何其他文件中要求的单个文件。它返回一个将任何 Lua 值转换为人类可读字符串的函数:
local inspect = require('inspect')
print(inspect(1,2,3)) -- 1, 2, 3
print(inspect(a=1,b=2)
--
-- a = 1
-- b = 2
--
它正确缩进子表,并正确处理“递归表”(包含对其自身的引用的表),因此它不会陷入无限循环。它以合理的方式对值进行排序。它还打印元表信息。
问候!
【讨论】:
也许您应该将您的库添加到Lua Wiki。我看到你的库也打印元表,其他库没有。 问题是inspect.lua 并不真正适合“序列化”类别。它返回的文本不是有效的 Lua 代码;它应该用于调试/人工阅读。我想我可以在末尾添加一个小链接之类的。 将 inspect.lua 添加到 wiki。 请把这个放在 luarocks 上 @Hack-R 它在 luarocks 上:luarocks install inspect
【参考方案3】:
请随意浏览Lua Wiki on table serialization。它列出了如何将表转储到控制台的几种方法。
您只需要选择最适合您的。有很多方法可以做到,但我通常最终使用来自Penlight的一种:
> t = a = b = c = "Hello world!", 1 , 2, d = 3
> require 'pl.pretty'.dump(t)
a =
d =
3
,
b =
c = "Hello world!",
1
,
2
【讨论】:
愚蠢甚至更多的新手问题:我该如何安装像 pl.pretty 这样的扩展?如果我可以做一些类似 gem install 之类的事情,而不需要展开 tar 球并在我的 HD 上找到理想的位置来放置东西,那就太好了。有没有快速/无痛的“这样做”? Dah,我应该在发布最后一条评论之前查看主页!安装不像我希望的那样快速/无痛,但也不错。 penlight 照亮了我正在寻找的东西! @Cliff luarocks 安装手电筒luarocks install penlight
【参考方案4】:
找到这个:
-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
function tprint (tbl, indent)
if not indent then indent = 0 end
for k, v in pairs(tbl) do
formatting = string.rep(" ", indent) .. k .. ": "
if type(v) == "table" then
print(formatting)
tprint(v, indent+1)
elseif type(v) == 'boolean' then
print(formatting .. tostring(v))
else
print(formatting .. v)
end
end
end
从这里 https://gist.github.com/ripter/4270799
对我来说效果很好......
【讨论】:
【参考方案5】:我见过的大多数纯 lua 打印表函数都存在深度递归问题 并且在太深时往往会导致堆栈溢出。这个印刷品 我写的表函数没有这个问题。由于它处理连接的方式,它还应该能够处理非常大的表。在我个人对该函数的使用中,它在大约一秒钟内输出了 63k 行到文件。
输出还保留了lua语法,脚本可以很容易地修改 如果修改为允许,则通过将输出写入文件来进行简单的持久存储 仅要格式化的数字、布尔值、字符串和表格数据类型。
function print_table(node)
local cache, stack, output = ,,
local depth = 1
local output_str = "\n"
while true do
local size = 0
for k,v in pairs(node) do
size = size + 1
end
local cur_index = 1
for k,v in pairs(node) do
if (cache[node] == nil) or (cur_index >= cache[node]) then
if (string.find(output_str,"",output_str:len())) then
output_str = output_str .. ",\n"
elseif not (string.find(output_str,"\n",output_str:len())) then
output_str = output_str .. "\n"
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = ""
local key
if (type(k) == "number" or type(k) == "boolean") then
key = "["..tostring(k).."]"
else
key = "['"..tostring(k).."']"
end
if (type(v) == "number" or type(v) == "boolean") then
output_str = output_str .. string.rep('\t',depth) .. key .. " = "..tostring(v)
elseif (type(v) == "table") then
output_str = output_str .. string.rep('\t',depth) .. key .. " = \n"
table.insert(stack,node)
table.insert(stack,v)
cache[node] = cur_index+1
break
else
output_str = output_str .. string.rep('\t',depth) .. key .. " = '"..tostring(v).."'"
end
if (cur_index == size) then
output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. ""
else
output_str = output_str .. ","
end
else
-- close the table
if (cur_index == size) then
output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. ""
end
end
cur_index = cur_index + 1
end
if (size == 0) then
output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. ""
end
if (#stack > 0) then
node = stack[#stack]
stack[#stack] = nil
depth = cache[node] == nil and depth + 1 or depth - 1
else
break
end
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = table.concat(output)
print(output_str)
end
这是一个例子:
local t =
["abe"] = 1,2,3,4,5,
"string1",
50,
["depth1"] = ["depth2"] = ["depth3"] = ["depth4"] = ["depth5"] = ["depth6"] = ["depth7"]= ["depth8"] = ["depth9"] = ["depth10"] = 1000, 900, 800,700,600,500, 400 , 300, 200, 100,
["ted"] = true,false,"some text",
"string2",
[function() return end] = function() return end,
75
print_table(t)
输出:
[1] = 'string1',
[2] = 50,
[3] = 'string2',
[4] = 75,
['abe'] =
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4,
[5] = 5
,
['function: 06472B70'] = 'function: 06472A98',
['depth1'] =
[1] = 100,
['depth2'] =
[1] = 200,
['depth3'] =
[1] = 300,
['depth4'] =
[1] = 400,
['depth5'] =
[1] = 500,
['depth6'] =
[1] = 600,
['depth7'] =
[1] = 700,
['depth8'] =
[1] = 800,
['depth9'] =
[1] = 900,
['depth10'] =
[1] = 1000
,
['ted'] =
[1] = true,
[2] = false,
[3] = 'some text'
【讨论】:
tab
函数过于复杂。它基本上只是string.repeat('\t', amt)
,但性能要差得多。【参考方案6】:
如前所述,您必须编写它。 这是我的简陋版本:(超级基本的)
function tprint (t, s)
for k, v in pairs(t) do
local kfmt = '["' .. tostring(k) ..'"]'
if type(k) ~= 'string' then
kfmt = '[' .. k .. ']'
end
local vfmt = '"'.. tostring(v) ..'"'
if type(v) == 'table' then
tprint(v, (s or '')..kfmt)
else
if type(v) ~= 'string' then
vfmt = tostring(v)
end
print(type(t)..(s or '')..kfmt..' = '..vfmt)
end
end
end
示例:
local mytbl = ['1']="a", 2, 3, b="c", t=d=1
tprint(mytbl)
输出(Lua 5.0):
table[1] = 2
table[2] = 3
table["1"] = "a"
table["t"]["d"] = 1
table["b"] = "c"
【讨论】:
非常原创!我喜欢它。【参考方案7】:最简单的方法,循环引用处理等等:
function dump(t, indent, done)
done = done or
indent = indent or 0
done[t] = true
for key, value in pairs(t) do
print(string.rep("\t", indent))
if type(value) == "table" and not done[value] then
done[value] = true
print(key, ":\n")
dump(value, indent + 2, done)
done[value] = nil
else
print(key, "\t=\t", value, "\n")
end
end
end
【讨论】:
PrintTable()
定义在哪里?
@MikeLyons 它是一个递归函数,我放错了 PrintTable 而不是 dump >.
【参考方案8】:
我使用自己的函数来打印表格的内容,但不确定它在您的环境中的转换效果如何:
---A helper function to print a table's contents.
---@param tbl table @The table to print.
---@param depth number @The depth of sub-tables to traverse through and print.
---@param n number @Do NOT manually set this. This controls formatting through recursion.
function PrintTable(tbl, depth, n)
n = n or 0;
depth = depth or 5;
if (depth == 0) then
print(string.rep(' ', n).."...");
return;
end
if (n == 0) then
print(" ");
end
for key, value in pairs(tbl) do
if (key and type(key) == "number" or type(key) == "string") then
key = string.format("[\"%s\"]", key);
if (type(value) == "table") then
if (next(value)) then
print(string.rep(' ', n)..key.." = ");
PrintTable(value, depth - 1, n + 4);
print(string.rep(' ', n)..",");
else
print(string.rep(' ', n)..key.." = ,");
end
else
if (type(value) == "string") then
value = string.format("\"%s\"", value);
else
value = tostring(value);
end
print(string.rep(' ', n)..key.." = "..value..",");
end
end
end
if (n == 0) then
print(" ");
end
end
【讨论】:
【参考方案9】:恐怕您必须自己编写代码。这是我写的,可能对你有用
function printtable(table, indent)
indent = indent or 0;
local keys = ;
for k in pairs(table) do
keys[#keys+1] = k;
table.sort(keys, function(a, b)
local ta, tb = type(a), type(b);
if (ta ~= tb) then
return ta < tb;
else
return a < b;
end
end);
end
print(string.rep(' ', indent)..'');
indent = indent + 1;
for k, v in pairs(table) do
local key = k;
if (type(key) == 'string') then
if not (string.match(key, '^[A-Za-z_][0-9A-Za-z_]*$')) then
key = "['"..key.."']";
end
elseif (type(key) == 'number') then
key = "["..key.."]";
end
if (type(v) == 'table') then
if (next(v)) then
printf("%s%s =", string.rep(' ', indent), tostring(key));
printtable(v, indent);
else
printf("%s%s = ,", string.rep(' ', indent), tostring(key));
end
elseif (type(v) == 'string') then
printf("%s%s = %s,", string.rep(' ', indent), tostring(key), "'"..v.."'");
else
printf("%s%s = %s,", string.rep(' ', indent), tostring(key), tostring(v));
end
end
indent = indent - 1;
print(string.rep(' ', indent)..'');
end
【讨论】:
感谢您的回复。我试过这个,我得到:尝试调用全局“排序”(一个零值) 将sort
更改为 table.sort
... 一定有一个 local sort = table.sort
在代码中的某处取自。
你得有点想象力!为方便起见,有许多符号从库表空间复制到 _G。 sort
是 table.sort
的副本,strrep
是 string.rep
,strmatch
是 string.match
等等。如果还有其他内容,请告诉我,我会更改答案。
对不起,我也有很深的表格网,因为我自己尝试递归结构遇到了堆栈溢出。 (没有双关语的意思!)我正在努力放松我的递归并使用适当的尾调用,但我在此处发布时感到沮丧。
你一般不能从这样的函数中删除递归,因为它不是结束递归的。要么使用使用更大堆栈构建的 Lua,要么使用 Lua 表实现相同的算法来存储递归堆栈。【参考方案10】:
metalua的table.tostring
方法其实很完善。它处理嵌套表,缩进级别是可变的,...
见https://github.com/fab13n/metalua/blob/master/src/lib/metalua/table2.lua
【讨论】:
警告,即使设置了也不会转储索引0【参考方案11】:这是我的版本,支持排除表和用户数据
-- Lua Table View by Elertan
table.print = function(t, exclusions)
local nests = 0
if not exclusions then exclusions = end
local recurse = function(t, recurse, exclusions)
indent = function()
for i = 1, nests do
io.write(" ")
end
end
local excluded = function(key)
for k,v in pairs(exclusions) do
if v == key then
return true
end
end
return false
end
local isFirst = true
for k,v in pairs(t) do
if isFirst then
indent()
print("|")
isFirst = false
end
if type(v) == "table" and not excluded(k) then
indent()
print("|-> "..k..": "..type(v))
nests = nests + 1
recurse(v, recurse, exclusions)
elseif excluded(k) then
indent()
print("|-> "..k..": "..type(v))
elseif type(v) == "userdata" or type(v) == "function" then
indent()
print("|-> "..k..": "..type(v))
elseif type(v) == "string" then
indent()
print("|-> "..k..": ".."\""..v.."\"")
else
indent()
print("|-> "..k..": "..v)
end
end
nests = nests - 1
end
nests = 0
print("### START TABLE ###")
for k,v in pairs(t) do
print("root")
if type(v) == "table" then
print("|-> "..k..": "..type(v))
nests = nests + 1
recurse(v, recurse, exclusions)
elseif type(v) == "userdata" or type(v) == "function" then
print("|-> "..k..": "..type(v))
elseif type(v) == "string" then
print("|-> "..k..": ".."\""..v.."\"")
else
print("|-> "..k..": "..v)
end
end
print("### END TABLE ###")
end
这是一个例子
t =
location =
x = 10,
y = 20
,
size =
width = 100000000,
height = 1000,
,
name = "Sidney",
test =
hi = "lol",
,
anotherone =
1,
2,
3
table.print(t, "test" )
打印:
### START TABLE ###
root
|-> size: table
|
|-> height: 1000
|-> width: 100000000
root
|-> location: table
|
|-> y: 20
|-> x: 10
root
|-> anotherone: table
|
|-> 1: 1
|-> 2: 2
|-> 3: 3
root
|-> test: table
|
|-> hi: "lol"
root
|-> name: "Sidney"
### END TABLE ###
请注意,根目录不会删除排除项
【讨论】:
【参考方案12】:我想提一下两种解决方案:一种快速而肮脏的解决方案,另一种可以正确转义所有键和值但更大的解决方案
简单快速的解决方案(仅用于“安全”输入):
local function format_any_value(obj, buffer)
local _type = type(obj)
if _type == "table" then
buffer[#buffer + 1] = '"'
for key, value in next, obj, nil do
buffer[#buffer + 1] = tostring(key) .. '":'
format_any_value(value, buffer)
buffer[#buffer + 1] = ',"'
end
buffer[#buffer] = '' -- note the overwrite
elseif _type == "string" then
buffer[#buffer + 1] = '"' .. obj .. '"'
elseif _type == "boolean" or _type == "number" then
buffer[#buffer + 1] = tostring(obj)
else
buffer[#buffer + 1] = '"???' .. _type .. '???"'
end
end
用法:
local function format_as_json(obj)
if obj == nil then return "null" else
local buffer =
format_any_value(obj, buffer)
return table.concat(buffer)
end
end
local function print_as_json(obj)
print(_format_as_json(obj))
end
print_as_json 1, 2, 3
print_as_json(nil)
print_as_json("string")
print_as_json [1] = 1, [2] = 2, three = true , four = "four"
键/值转义的正确解决方案
我为这个特定用例用纯 Lua 编写的小库:https://github.com/vn971/fast_json_encode
或者特别是包含格式化程序和打印机的 1 个文件:https://github.com/vn971/fast_json_encode/blob/master/json_format.lua
【讨论】:
这实际上是我一直在寻找的东西,尽管它并不是操作员所要求的。感谢您提供简单的解决方案。使其更易于在 NodeMCU 等空间受限的 Lua 环境中使用。 如果键或值包含引号、换行符或控制字符,这将产生无效的 JSON。 @CherryDT 哦,好话,谢谢。我绝对应该明确表示它不会(根本)逃避任何事情。对于实际正确但较慢的解决方案,这应该有效:github.com/vn971/fast_json_encode/blob/master/json_format.lua 如果它对您有效/无效,希望听到任何反馈。【参考方案13】:--~ print a table
function printTable(list, i)
local listString = ''
--~ begin of the list so write the
if not i then
listString = listString .. ''
end
i = i or 1
local element = list[i]
--~ it may be the end of the list
if not element then
return listString .. ''
end
--~ if the element is a list too call it recursively
if(type(element) == 'table') then
listString = listString .. printTable(element)
else
listString = listString .. element
end
return listString .. ', ' .. printTable(list, i + 1)
end
local table = 1, 2, 3, 4, 5, 'a', 'b', 'G', 'F'
print(printTable(table))
嗨,伙计,我用纯 Lua 编写了一个简单的代码,它有一个错误(在列表的最后一个元素之后写一个逗号)但是我如何快速将它作为原型编写,我会让它适应你满足您的需求。
【讨论】:
【参考方案14】:添加另一个版本。这一个尝试也对用户数据进行迭代。
function inspect(o,indent)
if indent == nil then indent = 0 end
local indent_str = string.rep(" ", indent)
local output_it = function(str)
print(indent_str..str)
end
local length = 0
local fu = function(k, v)
length = length + 1
if type(v) == "userdata" or type(v) == 'table' then
output_it(indent_str.."["..k.."]")
inspect(v, indent+1)
else
output_it(indent_str.."["..k.."] "..tostring(v))
end
end
local loop_pairs = function()
for k,v in pairs(o) do fu(k,v) end
end
local loop_metatable_pairs = function()
for k,v in pairs(getmetatable(o)) do fu(k,v) end
end
if not pcall(loop_pairs) and not pcall(loop_metatable_pairs) then
output_it(indent_str.."[[??]]")
else
if length == 0 then
output_it(indent_str.."")
end
end
end
【讨论】:
【参考方案15】:转成json再打印。
local json = require('cjson')
json_string = json.encode(this_table)
print (json_string)
【讨论】:
【参考方案16】:在 lua 中转储表的简单示例
我建议使用serpent.lua
local function parser(value, indent, subcategory)
local indent = indent or 2
local response = '(\n'
local subcategory = type(subcategory) == 'number' and subcategory or indent
for key, value in pairs(value) do
if type(value) == 'table' then
value = parser(value, indent, subcategory + indent)
elseif type(value) == 'string' then
value = '\''.. value .. '\''
elseif type(value) ~= 'number' then
value = tostring(value)
end
if type(tonumber(key)) == 'number' then
key = '[' .. key .. ']'
elseif not key:match('^([A-Za-z_][A-Za-z0-9_]*)$') then
key = '[\'' .. key .. '\']'
end
response = response .. string.rep(' ', subcategory) .. key .. ' = ' .. value .. ',\n'
end
return response .. string.rep(' ', subcategory - indent) .. ')'
end
示例
response = parser1,2,3, ok = 10,
print(response)
结果
(
[1] = 1,
[2] = 2,
[3] = 3,
[4] = (
[1] = (),
ok = 10
)
)
【讨论】:
【参考方案17】:我已经谦虚地修改了一点 Alundaio 代码:
-- by Alundaio
-- KK modified 11/28/2019
function dump_table_to_string(node, tree, indentation)
local cache, stack, output = ,,
local depth = 1
if type(node) ~= "table" then
return "only table type is supported, got " .. type(node)
end
if nil == indentation then indentation = 1 end
local NEW_LINE = "\n"
local TAB_CHAR = " "
if nil == tree then
NEW_LINE = "\n"
elseif not tree then
NEW_LINE = ""
TAB_CHAR = ""
end
local output_str = "" .. NEW_LINE
while true do
local size = 0
for k,v in pairs(node) do
size = size + 1
end
local cur_index = 1
for k,v in pairs(node) do
if (cache[node] == nil) or (cur_index >= cache[node]) then
if (string.find(output_str,"",output_str:len())) then
output_str = output_str .. "," .. NEW_LINE
elseif not (string.find(output_str,NEW_LINE,output_str:len())) then
output_str = output_str .. NEW_LINE
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = ""
local key
if (type(k) == "number" or type(k) == "boolean") then
key = "["..tostring(k).."]"
else
key = "['"..tostring(k).."']"
end
if (type(v) == "number" or type(v) == "boolean") then
output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = "..tostring(v)
elseif (type(v) == "table") then
output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = " .. NEW_LINE
table.insert(stack,node)
table.insert(stack,v)
cache[node] = cur_index+1
break
else
output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = '"..tostring(v).."'"
end
if (cur_index == size) then
output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. ""
else
output_str = output_str .. ","
end
else
-- close the table
if (cur_index == size) then
output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. ""
end
end
cur_index = cur_index + 1
end
if (size == 0) then
output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. ""
end
if (#stack > 0) then
node = stack[#stack]
stack[#stack] = nil
depth = cache[node] == nil and depth + 1 or depth - 1
else
break
end
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = table.concat(output)
return output_str
end
然后:
print(dump_table_to_string("AA", true,3))
print(dump_table_to_string("AA","BB", true,3))
print(dump_table_to_string("AA","BB"))
print(dump_table_to_string("AA","BB",false))
print(dump_table_to_string("AA","BB",22,33,true,2))
给予:
only table type is supported, got string
[1] = 'AA',
[2] = 'BB'
[1] = 'AA',
[2] = 'BB'
[1] = 'AA',[2] = 'BB'
[1] = 'AA',
[2] = 'BB',
[3] =
[1] = 22,
[2] = 33
【讨论】:
【参考方案18】:现在print函数可以打印(平面)表格了!
oprint = print -- origin print
print = function (...)
if type(...) == "table" then
local str = ''
local amount = 0
for i,v in pairs(...) do
amount=amount+1
local pre = type(i) == "string" and i.."=" or ""
str = str .. pre..tostring(v) .. "\t"
end
oprint('#'..amount..':', str)
else
oprint(...)
end
end
例如:
print (x=7, y=9, w=11, h="height", 7, 8, 9)
打印:
#7:7 8 9 y=9 x=7 h=高度 w=11
同样的方法可以只是新函数tostring:
otostring = tostring -- origin tostring
tostring = function (...)
if type(...) == "table" then
local str = ''
for i,v in pairs(...) do
local pre = type(i) == "string" and i.."=" or ""
str = str .. pre..tostring(v) .. ", "
end
str = str:sub(1, -3)
return str..''
else
return otostring(...)
end
end
【讨论】:
以上是关于如何将表格转储到控制台?的主要内容,如果未能解决你的问题,请参考以下文章
如何在运行 pytest 时让 pycharm 转储 stdout 和 stderr 到控制台?
我如何将Postgres DDL转储到可粘贴到Google表格中的CSV中?