多分こんなの既に無限にあるんだろうなと思いつつ、ネットで見付けた物を元にして自分的に丁度良い程度のを作った。
リンク
- 私のコードの公開先:
- [Lua] Print table contents recursively and more usefully
https://gist.github.com/Phroneris/56c1edaf3186b09d289184b8188098a4
- [Lua] Print table contents recursively and more usefully
- 参考元というか改変元:
- [Lua] Print table contents recursively
https://gist.github.com/hashmal/874792
- [Lua] Print table contents recursively
コード
Qiitaのシンタックスハイライトが記事公開時点では構文エラーっぽくなっているが、ちゃんと動くのでご安心を。
function tprint(tbl, indent, tprinted)
indent = indent or 0
tprinted = tprinted or { [tostring(tbl)] = true }
local indentstr = string.rep(" ", indent)
if not next(tbl) then
print(indentstr .. "(empty)")
else
for k, v in pairs(tbl) do
local vstr = tostring(v)
local header = string.format("%s[%s] ", indentstr, k)
if type(v) == "table" then
if tprinted[vstr] then
print(header .. vstr .. " (already printed)")
else
tprinted[vstr] = true
print(header .. vstr)
tprinted = tprint(v, indent + 1, tprinted)
end
else
if type(v) == "string" then
v = v:gsub("\\", "\\\\"):gsub('"', '\\"')
:gsub("\n", "\\n"):gsub("\r", "\\r")
:gsub("\t", "\\t"):gsub("\v", "\\v")
:gsub("\a", "\\a"):gsub("\b", "\\b"):gsub("\f", "\\f")
vstr = '"' .. v .. '"'
end
print(header .. vstr)
end
end
end
return tprinted
end
機能
- テーブルを再帰的に展開してプリント
- 値は
tostring()してどんな型でも安全に- 値
vが関数等の場合にheader .. vなんてできない
- 値
- 空のテーブルを明示
-
_Gの様な自己参照的なテーブルにおける無限ループを防ぐため、プリント済みのテーブルは再展開しない-
_G._Gや_G.package.loaded._Gには_Gが入っているし、_G.package.loaded.packageには_G.packageが入っているので、馬鹿正直な全展開は無限ループに陥る
-
- 読みやすさやインデント構造保持のため、文字列をリテラル風に整形
-
"文字列"のようにダブルクォート -
\、"をエスケープ - 主要な制御文字(改行等)をエスケープシーケンスで表現
- 特に改行がそのままだと、インデントの深さでテーブルの深さを表すという出力整形法則が崩れる
-
使用例
local tbl = { 42, true, a = "A", c = "C:\\Windows" }
tbl.lines = [[Lorem
ipsum
dolor]]
tbl.empty = {}
tbl.nested = { 100, 200, { 300 } }
tbl.square = function(v) return v * v end
tbl.self = tbl
tprint(tbl)
標準出力
[1] 42
[2] true
[self] table: 0x23779f00 (already printed)
[square] function: 0x2376a2d0
[empty] table: 0x2376a338
(empty)
[nested] table: 0x23766678
[1] 100
[2] 200
[3] table: 0x2376a298
[1] 300
[lines] "Lorem\nipsum\ndolor"
[c] "C:\\Windows"
[a] "A"
キー順がまちまちなのは pairs() の仕様なのでご愛嬌。
応用: キー順を固定したい場合
順序を固定したければ、一旦キーだけテーブルに収集してソートしてからそれをループすれば良い。
ただし、数値と文字列のキーが混在すると attempt to compare string with number エラーが出るので、比較時には tostring() で正規化する。
また、数値っぽい文字列キーがあると結果を見て混乱するので、文字列キーも "文字列リテラル風" にする方が親切だろう。表示が少しクドくなるが。
function tprint(tbl, indent, tprinted)
indent = indent or 0
tprinted = tprinted or { [tostring(tbl)] = true }
local indentstr = string.rep(" ", indent)
if not next(tbl) then
print(indentstr .. "(empty)")
else
local keys = {}
for k, _ in pairs(tbl) do table.insert(keys, k) end
table.sort(
keys,
function(a, b)
local aType, bType = type(a), type(b)
if aType == bType then return a < b
else return aType == "number"
end
end
)
local function formatstr(v)
if type(v) == "string" then
v = v:gsub("\\", "\\\\"):gsub('"', '\\"')
:gsub("\n", "\\n"):gsub("\r", "\\r")
:gsub("\t", "\\t"):gsub("\v", "\\v")
:gsub("\a", "\\a"):gsub("\b", "\\b"):gsub("\f", "\\f")
return '"' .. v .. '"'
else
return tostring(v)
end
end
for _, k in ipairs(keys) do
local v = tbl[k]
local vstr = formatstr(v)
local header = string.format("%s[%s] ", indentstr, formatstr(k))
if type(v) == "table" then
if tprinted[vstr] then
print(header .. vstr .. " (already printed)")
else
tprinted[vstr] = true
print(header .. vstr)
tprinted = tprint(v, indent + 1, tprinted)
end
else
print(header .. vstr)
end
end
end
return tprinted
end
使用例
local tbl = { 42, true, a = "A", c = "C:\\Windows", ["01"] = 1 }
tbl.lines = [[Lorem
ipsum
dolor]]
tbl.empty = {}
tbl.nested = { 100, 200, { 300 } }
tbl.square = function(v) return v * v end
tbl.self = tbl
tprint(tbl)
使用例: 標準出力
[1] 42
[2] true
["01"] 1
["a"] "A"
["c"] "C:\\Windows"
["empty"] table: 0x2373aaa8
(empty)
["lines"] "Lorem\nipsum\ndolor"
["nested"] table: 0x2f271dc0
[1] 100
[2] 200
[3] table: 0x30ac8650
[1] 300
["self"] table: 0x237648e0 (already printed)
["square"] function: 0x30a71438
おわり