Как выгрузить таблицу в консоль?

109

У меня проблемы с отображением содержимого таблицы, содержащей вложенные таблицы (глубиной n). Я хотел бы просто выгрузить его на стандартный вывод или в консоль с помощью printоператора или чего-то быстрого и грязного, но я не могу понять, как это сделать. Я ищу приблизительный эквивалент, который я бы получил при печати с NSDictionaryиспользованием gdb.

Клифф
источник

Ответы:

56

Не стесняйтесь просматривать Lua Wiki по сериализации таблиц . В нем перечислены несколько способов выгрузки таблицы в консоль.

Вам просто нужно выбрать, какой из них вам больше подходит. Есть много способов сделать это, но обычно я использую способ от 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
  }
}
Михал Коттман
источник
6
Тупой и даже более вопрос новичка: как мне установить расширение, такое как pl.pretty? Было бы хорошо, если бы я мог просто сделать что-то вроде установки драгоценного камня, не возясь с разворачивающимися шариками смолы и не находя идеального места на моем HD для размещения вещей. Есть ли быстрый / безболезненный способ «сделать это»?
Cliff
1
Да уж, мне следовало заглянуть на главную страницу перед тем, как опубликовать последний комментарий! Установка не такая быстрая / безболезненная, как я надеялся, но не так уж плохо.
Клифф
фонарик проливает свет на то, что я искал!
Клифф
7
@Cliff luarocks, чтобы установить фонарик
vagabond
101

Я знаю, что этот вопрос уже отмечен как ответ, но позвольте мне подключить сюда мою собственную библиотеку. Он называется 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; он должен использоваться для отладки / чтения человеком. Я полагаю, я мог бы добавить небольшую ссылку в конце или что-то в этом роде.
kikito
1
В вики добавлен файл inspect.lua.
kikito
Пожалуйста, поставьте это на luarocks
Hack-R
3
@ Hack-R это на луароках:luarocks install inspect
kikito
87

Я нашел это полезным. Потому что при рекурсии он может печатать и вложенные таблицы. Это не дает наилучшего форматирования на выходе, но для такой простой функции ее трудно превзойти при отладке.

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 Street , ["phone"] = 123456, ["name"] = Wilma,}, [3] = {["address"] = 17 Long Street, ["phone"] = 123457, ["name"] = Барни, },}

Мэтт
источник
1
Молодец, что поделился тем, для чего не нужна внешняя библиотека.
Джулиан Найт,
На действительно большой таблице ваша функция выдает ошибку stackoverflow
Херрготт,
21

нашел это:

-- 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

у меня неплохо работает ...

JCH2k
источник
19

Большинство чистых табличных функций печати lua, которые я видел, имеют проблему с глубокой рекурсией и, как правило, вызывают переполнение стека, когда заходят слишком глубоко. Эта функция таблицы печати, которую я написал, не имеет этой проблемы. Он также должен быть способен обрабатывать действительно большие таблицы из-за того, как он обрабатывает конкатенацию. В моем личном использовании этой функции она выводила 63 тыс. Строк в файл примерно за секунду.

Вывод также сохраняет синтаксис 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)но гораздо менее эффективно.
Вал говорит Reinstate Monica
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"
Тотока
источник
1
Очень оригинально! Мне это нравится.
Джек Гиффин
2

table.tostringMetehod из metalua на самом деле очень полный. Он имеет дело с вложенными таблицами, уровень отступов можно изменять, ... См. Https://github.com/fab13n/metalua/blob/master/src/lib/metalua/table2.lua

картбен
источник
2
предупреждение, он не будет сбрасывать индекс 0, даже если он установлен
Янус Троелсен
2

Это моя версия, которая поддерживает исключение таблиц и пользовательских данных.

-- 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 ###

Обратите внимание, что корень не удаляет исключения

Элертан
источник
2

Форматировать как JSON (позже в IDE можно "украсить"):

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"}

Кстати, я также написал несколько других решений: очень быстрое и одно с экранированием специальных символов: https://github.com/vn971/fast_json_encode

Василий Новиков
источник
На самом деле это именно то, что я искал, хотя операционист спрашивал не об этом. Спасибо за такое простое решение. Облегчает использование в ограниченных по объему средах Lua, таких как NodeMCU.
Sawtaytoes
1

Боюсь, вы должны сами это кодировать. Я написал это, и это может быть вам полезно

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
Бородин
источник
1
Спасибо за ответ. Я попробовал это и получил: попытка вызвать глобальную сортировку (нулевое значение)
Клифф,
Измените sortна table.sort... Должно быть, local sort = table.sortгде-то в коде есть место, откуда это взято.
Михал Коттман,
Вы должны быть немного изобретательны! Ряд символов скопирован из табличного пространства библиотеки в _G для удобства. sortявляется копией table.sort, strrepесть string.rep, strmatchесть string.matchи т. д. Сообщите мне, если они появятся, и я изменю свой ответ.
Бородин
Извините, у меня также есть довольно глубокая сетка таблиц, поскольку мои собственные попытки рекурсии структуры встретились с переполнением стека. (Никакой каламбур!) Я бился головой, пытаясь раскрутить свою рекурсию и использовать правильные хвостовые вызовы, но я был разочарован, когда опубликовал здесь.
Cliff
Вы не можете вообще удалить рекурсию из такой функции, поскольку она не является конечной рекурсией. Либо используйте Lua, построенный с большим стеком, либо реализуйте тот же алгоритм, используя таблицу Lua для хранения стека рекурсии.
Бородин
1
--~ 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, в нем есть ошибка (напишите кому после последнего элемента списка), но как я написал его быстро в качестве прототипа, я позволю вам адаптировать его к вашему потребности.

Joqus
источник
1

Добавление другой версии. Этот также пытается перебирать пользовательские данные.

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
phil294
источник
1

Я использую свою собственную функцию для печати содержимого таблицы, но не уверен, насколько хорошо она соответствует вашей среде:

---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
Mayron
источник
-1

Я скромно изменил код 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
  }
}
kris2k
источник