Need Addon help fixing Name Detection in Chat - lua
The following code is suppose to change a user name say Player in any text example: '[Player2]: Hi Player' into '[Player2]: Hi >Player<' with Player in red and >< symbols in yellow. Problem is its not detecting other players saying it and is just plain coloring them by class and warning when I say my name when it should be the other way around.
I understand there is likely Global Leaks and such as this is a beta phase addon at the moment and I am constantly working on rewriting functions to make them better but for now I am trying to get everything working first. If you can simplify the Name detection and change it without messing with links in WoW I would take the advice and I will throw a comment line in the code to show your name for credit and in the main addon page. Otherwise I just need the fix to find out why its not working as intended.
Here is the code:
local _PlayerName2 = CM2.StripSpecial(UnitName("player"), true)
_PlayerName2 = _PlayerName2:lower()
local _PlayerName, _ = UnitName("player")
--Detect if the current character said their own name. Block Warnings.
local throwWarn = true
if ChatAuthor == curPlayer then
--Player said something.
throwWarn = false
--CM2.Print("Disabled Warnings")
end
if text == nil then
print("Text Nil, Check URL Detection Code")
end
--Color own name in your chat. Really only sender name because all other references will
--be tied to the alert.
if throwWarn == false and text:find(" "..curPlayer.." ") ~= nil then
text = CM2.far(text, curPlayer, CM2.ColorName(CM2_Nick[plainPlayerName][2], plainPlayerName))
end
if throwWarn == false and text:find(case_insensitive_pattern(plainPlayerName)) ~= nil then
text = CM2.far(text, plainPlayerName, CM2.ColorName(CM2_Nick[plainPlayerName][2], plainPlayerName))
end
--Detect and alert when playername is spoken
local pos = 0
if throwWarn == true and text:find(" ".._PlayerName.." ") ~= nil then
--Detect if the Original Player name was said.
text = text:gsub(" ".._PlayerName.." ", " "..CHATMOD_COLOR["YELLOW"]..">"..CHATMOD_COLOR["RED"].._PlayerName..CHATMOD_COLOR["YELLOW"].."<\124r ")
UIErrorsFrame:AddMessage(text, red, green, blue, nil, UIERRORS_HOLD_TIME)
PlaySound("FriendJoinGame")
elseif throwWarn == true and text:find(" "..case_insensitive_pattern(plainPlayerName).." ") ~= nil then
--Detect if the stripped down Player Name was said.
text = text:gsub(" ".._PlayerName2.." ", " "..CHATMOD_COLOR["YELLOW"]..">"..CHATMOD_COLOR["RED"].._PlayerName2..CHATMOD_COLOR["YELLOW"].."<\124r ")
UIErrorsFrame:AddMessage(text, red, green, blue, nil, UIERRORS_HOLD_TIME)
PlaySound("FriendJoinGame")
end
if text == nil then
print("Text Nil, Check playername highlighter Code")
end
-- Color Nicks
if CM2_Options["ColorNicks"] then
-- Hold Original unmodified words for later use
temp2 = text
temp2 = CM2.StripSpecial(temp2)
temp = CM2.StripSpecial(text)
temp = string.gsub(temp, "%]%[", " ")
temp = string.gsub(temp, "[^a-zA-Z0-9%s]", "")
words = CM2.GetWords(temp)
words2 = CM2.GetWords(temp:lower())
for word = 1, #words do
-- Cant be the player name or it locks up the client... Go figure...
if words[word] ~= UnitName("player") and words[word] ~= plainPlayerName then
--print(words[word]:lower())
if CM2_Nick[words[word]:lower()] ~= nil then
--{level, class, guild, realm, name} Nick Layout. Name is the unfiltered name.
local newWord = CM2.ColorName(CM2_Nick[words[word]:lower()][2], words[word]:lower())
word2find = words[word]
pos = temp2:find(word2find) or temp:find(words2[word])
if newWord ~= nil then
--replace with find code.
text = CM2.far(text, word2find, newWord)
end
end
end
end
end
Function CM2.far:
function CM2.far(str, fstr, rstr)
if (str and type(str) == "string") and (fstr and type(fstr) == "string") and (rstr and type(rstr) == "string") then
--And space at the end so gfind will find till the end.
--In case a match is there.
str = str .. " "
local pos = nil
for x in str:gmatch("[^%:]"..case_insensitive_pattern(fstr).."[^%:]") do
if pos ~= nil and pos ~= str:find("[^%:]"..case_insensitive_pattern(fstr).."[^%:]", pos+1) then
str = str:sub(1, pos) .. rstr .. str:sub(pos + #fstr + 1)
--print(str)
pos = str:find("[^%:]"..case_insensitive_pattern(fstr).."[^%:]", pos+1)
elseif pos == nil then
pos = str:find("[^%:]"..case_insensitive_pattern(fstr).."[^%:]")
str = str:sub(1, pos) .. rstr .. str:sub(pos + #fstr + 1)
pos = str:find("[^%:]"..case_insensitive_pattern(fstr).."[^%:]", pos+1)
--print(str)
end
end
str = str:sub(1, #str - 1)
return str
end
end
Function CM2.StripSpecial:
function CM2.StripSpecial(msg, player)
PlayerStripped = 0
--Strips out all special characters such as Ö and the like.
--Should only be used for being returned to a temp string unless replacement is required.
if msg ~= nil and type(msg) == "string" then
for x=1, #msg do
local CharVal = string.byte(string.sub(msg,x,x+1), -1)
--local StrTab = {}
--for a=1, #msg do
-- StrTab:Insert(
--print("Debug: "..string.byte(string.sub(msg,x,x+1)))
--print(CharVal)
if CharVal ~= nil then
if 146 <= CharVal and CharVal <= 150 then
msg = StringReplace(msg, x, "O")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 178 <= CharVal and CharVal <= 182 then
msg = StringReplace(msg, x, "o")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 128 <= CharVal and CharVal <= 134 then
msg = StringReplace(msg, x, "A")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 160 <= CharVal and CharVal <= 166 then
msg = StringReplace(msg, x, "a")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 136 <= CharVal and CharVal <= 139 then
msg = StringReplace(msg, x, "E")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 168 <= CharVal and CharVal <= 171 then
msg = StringReplace(msg, x, "e")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 153 <= CharVal and CharVal <= 156 then
msg = StringReplace(msg, x, "U")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 185 <= CharVal and CharVal <= 188 then
msg = StringReplace(msg, x, "u")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 140 <= CharVal and CharVal <= 143 then
msg = StringReplace(msg, x, "I")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 172 <= CharVal and CharVal <= 175 then
msg = StringReplace(msg, x, "i")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 135 == CharVal then
msg = StringReplace(msg, x, "C")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 167 == CharVal then
msg = StringReplace(msg, x, "c")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 144 == CharVal then
msg = StringReplace(msg, x, "D")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 176 == CharVal then
msg = StringReplace(msg, x, "o")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 152 == CharVal then
msg = StringReplace(msg, x, "O")
if player then
PlayerStripped = PlayerStripped + 1
end
elseif 184 == CharVal then
msg = StringReplace(msg, x, "o")
if player then
PlayerStripped = PlayerStripped + 1
end
end
end
end
end
return msg
end
Function CM2.GetWords:
function CM2.GetWords(str)
if type(str) == "string" then
local results = {}
for word in string.gmatch(str, "%S+", 9) do
table.insert(results, word)
end
return results
end
end
Function case_insensitive_pattern:
function case_insensitive_pattern(pattern)
-- find an optional '%' (group 1) followed by any character (group 2)
local p = pattern:gsub("(%%?)(.)", function(percent, letter)
--print(percent, letter)
if percent ~= "" or (not letter:match("%a")) then
if letter ~= "-" then
-- if the '%' matched, or `letter` is not a letter, return "as is"
return percent .. letter
elseif letter == "-" then
return "%-"
end
else
if letter == "[" then
return "%["
elseif letter == "]" then
--print("hi")
return "%]"
end
-- else, return a case-insensitive character class of the matched letter
return string.format("[%s%s]", letter:lower(), letter:upper())
end
end)
return p
end
Function CM2.ColorName:
function CM2.ColorName(str, word)
--Using the class and word provided precolor it for chat.
if str == "MONK" then
word = "\124cff00ff96" .. CM2_Nick[word][5] .. "|r"
elseif str == "DEATH KNIGHT" then
word = "\124cffc41f3b" .. CM2_Nick[word][5] .. "|r"
elseif str == "DRUID" then
word = "\124cffff7d0a" .. CM2_Nick[word][5] .. "|r"
elseif str == "HUNTER" then
word = "\124cffabd473" .. CM2_Nick[word][5] .. "|r"
elseif str == "MAGE" then
word = "\124cff69ccf0" .. CM2_Nick[word][5] .. "|r"
elseif str == "PALADIN" then
word = "\124cfff58cba" .. CM2_Nick[word][5] .. "|r"
elseif str == "PRIEST" then
word = "\124cffffffff" .. CM2_Nick[word][5] .. "|r"
elseif str == "ROGUE" then
word = "\124cfffff569" .. CM2_Nick[word][5] .. "|r"
elseif str == "SHAMAN" then
word = "\124cff0070de" .. CM2_Nick[word][5] .. "|r"
elseif str == "WARLOCK" then
word = "\124cff9482c9" .. CM2_Nick[word][5] .. "|r"
elseif str == "WARRIOR" then
word = "\124cffc79c6e" .. CM2_Nick[word][5] .. "|r"
end
return word
end
Edit 1: Managed to fix self name warning but noticed that saying your full matching player name in chat is not being colored. I can deal with this myself. No changes we able to be made yet to fix the other players saying the players name warning yet.
Related
How to dump all _G table content
i want to dump _G table. in _G table has other table also dump (inline table). and i want have a good format. i have a example but use it dump _G table have some problem function print_table(node) -- to make output beautiful local function tab(amt) local str = "" for i=1,amt do str = str .. "\t" end return str end 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 = "[" ..type(k).. ":"..tostring(k).."]" else key = "['"..type(k).. ":"..tostring(k).."']" end if (type(v) == "number" or type(v) == "boolean") then output_str = output_str .. tab(depth) .. key .. " = "..tostring(v) elseif (type(v) == "table") then output_str = output_str .. tab(depth) .. key .. " = {\n" table.insert(stack,node) table.insert(stack,v) cache[node] = cur_index+1 break else output_str = output_str .. tab(depth) .. key .. " = '"..tostring(v).."'" end if (cur_index == size) then output_str = output_str .. "\n" .. tab(depth-1) .. "}" else output_str = output_str .. "," end else -- close the table if (cur_index == size) then output_str = output_str .. "\n" .. tab(depth-1) .. "}" end end cur_index = cur_index + 1 end if (size == 0) then output_str = output_str .. "\n" .. tab(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 this is result log ------start------- ['function:add_msg_define'] = 'function: 0x7c6baa98', ['number:BLEND_DST_RGB'] = 32968, ['function:_attachShader'] = 'function: 0x7c626cf0', ['number:INVALID_OPERATION'] = 1282, ['function:drawArrays'] = 'function: 0x7c627480', ['number:SRGB8_ALPHA8_EXT'] = 35907, ['number:CCW'] = 2 ------end------- i don't why how to fix this function
I use this: -- deep dumps the contents of the table and it's contents' contents function deepdump( tbl ) local checklist = {} local function innerdump( tbl, indent ) checklist[ tostring(tbl) ] = true for k,v in pairs(tbl) do print(indent..k,v,type(v),checklist[ tostring(tbl) ]) if (type(v) == "table" and not checklist[ tostring(v) ]) then innerdump(v,indent.." ") end end end print("=== DEEPDUMP -----") checklist[ tostring(tbl) ] = true innerdump( tbl, "" ) print("------------------") end https://gist.github.com/HoraceBury/9321964
facing issue in lua code
How can I match "words" mixed parenthesis delimited strings, on the basis that they are separated by whitespaces. EG: split_words_and_parenthesis("1791 (AR6K Async) S 2 ") --> {"1791","AR6K Async","S","2"} Here's my attempt: str = "1791 (AR6K Async) S 2 " for val in str:gmatch("%S+") do if str:gmatch("(" )then str:gsub("%b()" , function(s) val=s end) print(val) else print(val) end end output: (AR6K Async) (AR6K Async) (AR6K Async) (AR6K Async) (AR6K Async)
Can be solved using string.match if you know the format: str = "1791 (AR6K Async) S 2 " s1 = str:match("(%d%d%d%d)%s%(.*%)%s.+%s.+") s2 = str:match("%d%d%d%d%s(%(.*%))%s.+%s.+") s3 = str:match("%d%d%d%d%s%(.*%)%s(.+)%s.+") s4 = str:match("%d%d%d%d%s%(.*%)%s.+%s(.+)") print(s1) print(s2) print(s3) print(s4) Another solution that is generic and allows for variable number of entries (try it: simply past in a lua interpreter): function get_separate_words(str) local i = 1 local words = {} function get_parentheses_content(str,is_recursively_called) local i = 1 local function split(s, sep) local fields = {} local sep = sep or ":" local pattern = string.format("([^%s]+)", sep) string.gsub(s, pattern, function(c) fields[#fields + 1] = c end) return fields end for j = 1,#str do local c = string.sub(str,j,j) local d = string.sub(str,j+1,j+1) if j <= i then elseif c == "(" then i = j + #get_parentheses_content(string.sub(str,j+1,#str),true) + 2 elseif c == ")" and (is_recursively_called or (d == " ") or (not d)) then print('c') local parentheses_content = string.sub(str,1,j-1) return {parentheses_content} end end local parentheses_content = string.match(str,"^(.*)%)%s+[^)]*$") if parentheses_content then print('a') end parentheses_content = parentheses_content or string.match(str,"^(.*)%)$") if parentheses_content then print("A") return {parentheses_content} else print("B") return split("("..str," ") end end local function merge(table_a, table_b) table_a = table_a or {} table_b = table_b or {} for k_b, v_b in pairs(table_b) do if type(v_b) == "table" and type(table_a[k_b] or false) == "table" then merge(table_a[k_b], table_b[k_b]) else table_a[k_b] = v_b end end return table_a end for j = 1,#str do local c = string.sub(str,j,j) if j < i then elseif c == " " or j == #str then local word = string.gsub(string.sub(str,i,j)," ","") if #word > 0 then table.insert(words, word) print(word) end i = j+1 elseif c == "(" then local all_characters_after_opening_parentheses = string.sub(str,j+1,#str) local parentheses_content = get_parentheses_content(all_characters_after_opening_parentheses)[1] table.insert(words, parentheses_content) j= j+#parentheses_content+2 i = j end end return words end separate_words = get_separate_words("1791 (AR6(K As)ync) S 2 )") for k,v in ipairs(separate_words) do print(k,v) end
Is there a way to add a numerical value to a variable in lua in a for loop?
I made this program: a = math.random(0, 9) - 0 -- In this stage, if the result equals zero, that means it's a match b = math.random(0, 9) - 1 c = math.random(0, 9) - 2 d = math.random(0, 9) - 3 e = math.random(0, 9) - 4 f = math.random(0, 9) - 5 g = math.random(0, 9) - 6 h = math.random(0, 9) - 7 i = math.random(0, 9) - 8 j = math.random(0, 9) - 9 print("Enter the number of trials you want to simulate.") -- This is where I decide how many trials I want to do var = io.read() a_ = 0 -- This is where I hope to keep the number of "a" matches, number of "b" matches, etc. The frequency b_ = 0 c_ = 0 d_ = 0 e_ = 0 f_ = 0 g_ = 0 h_ = 0 i_ = 0 j_ = 0 for k = 1, var do -- One loop is a trial print("Trail #"..k) if a == 0 then print("a = match") elseif a ~= 0 then print("a = not a match") end if b == 0 then print("b = match") elseif b ~= 0 then print("b = not a match") end if c == 0 then print("c = match") elseif c ~= 0 then print("c = not a match") end if d == 0 then print("d = match") elseif d ~= 0 then print("d = not a match") end if e == 0 then print("e = match") elseif e ~= 0 then print("e = not a match") end if f == 0 then print("f = match") elseif f ~= 0 then print("f = not a match") end if g == 0 then print("g = match") elseif g ~= 0 then print("g = not a match") end if h == 0 then print("h = match") elseif h ~= 0 then print("h = not a match") end if i == 0 then print("i = match") elseif i ~= 0 then print("i = not a match") end if j == 0 then print("j = match") elseif j ~= 0 then print("j = not a match") end end while true do --This is just to keep the window open after the program is done so that I can observe the data, you can ignore this end As you can see, I tried to add one to a_, b_ and c_ every time it returns a result of zero, but it doesn't work, it there a way to do this? The reason I want to do this is for an AP stats class I'm taking, and this will make it a lot easier to do. I'm just doing a_, b_, c_ for now, once I solve this issue, I'll do all of them. Thanks for reading!
Assuming you want the simulation to run 'var' times, try this: math.randomseed(os.time()) local matchStorage = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} local randomNums = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} local function runSimulation(numTrial) print("\nRunning trial #: " .. numTrial) for index, value in pairs(randomNums) do local variable = math.random(0, 9) randomNums[index] = variable end for index, value in pairs(randomNums) do if randomNums[index] == 0 then matchStorage[index] = matchStorage[index] + 1 print("index " .. index .. " is a match.") else print("index " .. index .. " is not a match.") end end end do print("Enter the number of trials") numTrials = io.read() for index = 1, numTrials do runSimulation(index) end print("\nRESULTS:\n") for index, value in pairs(matchStorage) do print("index " .. index .. " frequency: " .. value) end end The number of times that one of the following 'randomNum' values contain a 0 will be stored in its corresponding 'matchStorage' index.
Issue with lua code in world of warcraft
-- Color Nicks if CM2_Options["ColorNicks"] then -- Hold Original unmodified words for later use local temp2 = ChatMod2_GetWords(text) local temp = ChatMod2_StripSpecial(text) temp = string.gsub(temp, "[^a-zA-Z0-9%s]", "") temp = temp:lower() local words = ChatMod2_GetWords(temp) for word = 1, #words do -- Cant be the player name or it locks up the client... Go figure... if words[word] ~= UnitName("player") then print(temp) if CM2_Nick[words[word]] ~= nil then --{level, class, guild, realm, name} Nick Layout. Name is the unfiltered name. local newWord = ChatMod2_ColorName(CM2_Nick[words[word]][2], words[word]) text = text:gsub(temp2[word], newWord) end end end end The above is causing World Of Warcraft to fail to load into the game. Because I lack the debugging ability from the program itself does anyone see why here? Below are the other functions mentioned in the above code: function ChatMod2_GetWords(str) local results = {} for word in string.gmatch(str, "%S+", 9) do table.insert(results, word) end return results end function ChatMod2_ColorName(str, word) --Using the class and word provided precolor it for chat. if str == "MONK" then word = "\124cff00ff96" .. CM2_Nick[word][5] .. "|r" elseif str == "DEATH KNIGHT" then word = "\124cffc41f3b" .. CM2_Nick[word][5] .. "|r" elseif str == "DRUID" then word = "\124cffff7d0a" .. CM2_Nick[word][5] .. "|r" elseif str == "HUNTER" then word = "\124cffabd473" .. CM2_Nick[word][5] .. "|r" elseif str == "MAGE" then word = "\124cff69ccf0" .. CM2_Nick[word][5] .. "|r" elseif str == "PALADIN" then word = "\124cfff58cba" .. CM2_Nick[word][5] .. "|r" elseif str == "PRIEST" then word = "\124cffffffff" .. CM2_Nick[word][5] .. "|r" elseif str == "ROGUE" then word = "\124cfffff569" .. CM2_Nick[word][5] .. "|r" elseif str == "SHAMAN" then word = "\124cff0070de" .. CM2_Nick[word][5] .. "|r" elseif str == "WARLOCK" then word = "\124cff9482c9" .. CM2_Nick[word][5] .. "|r" elseif str == "WARRIOR" then word = "\124cffc79c6e" .. CM2_Nick[word][5] .. "|r" end return word end function ChatMod2_StripSpecial(msg) --Strips out all special characters such as Ö and the like. --Should only be used for being returned to a temp string unless replacement is required. if msg ~= nil and type(msg) == "string" then for x=1, #msg do local CharVal = string.byte(string.sub(msg,x,x+1), -1) --local StrTab = {} --for a=1, #msg do -- StrTab:Insert( --print("Debug: "..string.byte(string.sub(msg,x,x+1))) --print(CharVal) if CharVal ~= nil then if 146 <= CharVal and CharVal <= 150 then msg = StringReplace(msg, x, "O") elseif 178 <= CharVal and CharVal <= 182 then msg = StringReplace(msg, x, "o") elseif 128 <= CharVal and CharVal <= 134 then msg = StringReplace(msg, x, "A") elseif 160 <= CharVal and CharVal <= 166 then msg = StringReplace(msg, x, "a") elseif 136 <= CharVal and CharVal <= 139 then msg = StringReplace(msg, x, "E") elseif 168 <= CharVal and CharVal <= 171 then msg = StringReplace(msg, x, "e") elseif 153 <= CharVal and CharVal <= 156 then msg = StringReplace(msg, x, "U") elseif 185 <= CharVal and CharVal <= 188 then msg = StringReplace(msg, x, "u") elseif 140 <= CharVal and CharVal <= 143 then msg = StringReplace(msg, x, "I") elseif 172 <= CharVal and CharVal <= 175 then msg = StringReplace(msg, x, "i") elseif 135 == CharVal then msg = StringReplace(msg, x, "C") elseif 167 == CharVal then msg = StringReplace(msg, x, "c") elseif 144 == CharVal then msg = StringReplace(msg, x, "D") elseif 176 == CharVal then msg = StringReplace(msg, x, "o") elseif 152 == CharVal then msg = StringReplace(msg, x, "O") elseif 184 == CharVal then msg = StringReplace(msg, x, "o") end end end end return msg end I appreciate any and all help.
ChatMod2_GetWords is clearly fine. ChatMod2_ColorName on the other hand will explode if CM2_Nick doesn't have a word entry or if that entry doesn't have an index 5. I haven't looked at ChatMod2_StripSpecial much yet.
How to dump a table to console?
I'm having trouble displaying the contents of a table which contains nested tables (n-deep). I'd like to just dump it to std out or the console via a print statement or something quick and dirty but I can't figure out how. I'm looking for the rough equivalent that I'd get when printing an NSDictionary using gdb.
If the requirement is "quick and dirty" I've found this one useful. Because of the recursion it can print nested tables too. It doesn't give the prettiest formatting in the output but for such a simple function it's hard to beat for debugging. 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 e.g. 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)) Produces the following output: 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"] = Barney,} ,}
I know this question has already been marked as answered, but let me plug my own library here. It's called inspect.lua, and you can find it here: https://github.com/kikito/inspect.lua It's just a single file that you can require from any other file. It returns a function that transforms any Lua value into a human-readable string: local inspect = require('inspect') print(inspect({1,2,3})) -- {1, 2, 3} print(inspect({a=1,b=2}) -- { -- a = 1 -- b = 2 -- } It indents subtables properly, and handles "recursive tables" (tables that contain references to themselves) correctly, so it doesn't get into infinite loops. It sorts values in a sensible way. It also prints metatable information. Regards!
Feel free to browse the Lua Wiki on table serialization. It lists several ways on how to dump a table to the console. You just have to choose which one suits you best. There are many ways to do it, but I usually end up using the one from 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 } }
found this: -- 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 from here https://gist.github.com/ripter/4270799 works pretty good for me...
Most pure lua print table functions I've seen have a problem with deep recursion and tend to cause a stack overflow when going too deep. This print table function that I've written does not have this problem. It should also be capable of handling really large tables due to the way it handles concatenation. In my personal usage of this function, it outputted 63k lines to file in about a second. The output also keeps lua syntax and the script can easily be modified for simple persistent storage by writing the output to file if modified to allow only number, boolean, string and table data types to be formatted. 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 Here is an example: 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) Output: { [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' } }
As previously mentioned, you have to write it. Here is my humble version: (super basic one) 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 example: local mytbl = { ['1']="a", 2, 3, b="c", t={d=1} } tprint(mytbl) output (Lua 5.0): table[1] = 2 table[2] = 3 table["1"] = "a" table["t"]["d"] = 1 table["b"] = "c"
I use my own function to print the contents of a table but not sure how well it translates to your environment: ---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
The simplest way, with circular reference handling and all: 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
There are 2 solutions that I want to mention: a quick&dirty one, and another which properly escapes all keys and values but is bigger Simple & fast solution (use only on "safe" inputs): 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 Usage: 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"} Correct solution with key/value escaping Small library that I wrote in pure Lua for this specific use-case: https://github.com/vn971/fast_json_encode Or specifically this 1 file that includes both a formatter and a printer: https://github.com/vn971/fast_json_encode/blob/master/json_format.lua
You have to code it yourself I'm afraid. I wrote this, and it may be of some use to you 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
The table.tostring metehod of metalua is actually very complete. It deals with nested tables, the indentation level is changeable, ... See https://github.com/fab13n/metalua/blob/master/src/lib/metalua/table2.lua
This is my version that supports excluding tables and userdata -- 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 This is an example t = { location = { x = 10, y = 20 }, size = { width = 100000000, height = 1000, }, name = "Sidney", test = { hi = "lol", }, anotherone = { 1, 2, 3 } } table.print(t, { "test" }) Prints: ### 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 ### Notice that the root doesn't remove exclusions
Made this version to print tables with identation. Can probably be extended to work recursively. function printtable(table, indent) print(tostring(table) .. '\n') for index, value in pairs(table) do print(' ' .. tostring(index) .. ' : ' .. tostring(value) .. '\n') end end
--~ 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)) Hi man, I wrote a siple code that do this in pure Lua, it has a bug (write a coma after the last element of the list) but how i wrote it quickly as a prototype I will let it to you adapt it to your needs.
Adding another version. This one tries to iterate over userdata as well. 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
Convert to json and then print. local json = require('cjson') json_string = json.encode(this_table) print (json_string)
simple example of dump a table in lua i suggest using 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 example response = parser{1,2,3, {ok = 10, {}}} print(response) result ( [1] = 1, [2] = 2, [3] = 3, [4] = ( [1] = (), ok = 10 ) )
here's my little snippet for that: --- Dump value of a variable in a formatted string -- --- #param o table Dumpable object --- #param tbs string|nil Tabulation string, ' ' by default --- #param tb number|nil Initial tabulation level, 0 by default --- #return string local function dump(o, tbs, tb) tb = tb or 0 tbs = tbs or ' ' if type(o) == 'table' then local s = '{' if (next(o)) then s = s .. '\n' else return s .. '}' end tb = tb + 1 for k,v in pairs(o) do if type(k) ~= 'number' then k = '"' .. k .. '"' end s = s .. tbs:rep(tb) .. '[' .. k .. '] = ' .. dump(v, tbs, tb) s = s .. ',\n' end tb = tb - 1 return s .. tbs:rep(tb) .. '}' else return tostring(o) end end
I have humbly modified a bit Alundaio code: -- 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 then: 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)) gives: 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 } }
Now the function print can print the (flat) tables! 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 For example: print ({x=7, y=9, w=11, h="height", 7, 8, 9}) prints: #7: 7 8 9 y=9 x=7 h=height w=11 The same way it can be just new function 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