Save Table To File

lua-users home
wiki

Save a Table to a string or file; Load a Table from string or file

Since building a string from a big table, uses more time than saving the table to file and loading it from there, one should use table.save( table, true ) to get the stringtable from a big table

Characteristics:

Files:wiki_insecure/users/chill/table.save-0.94.lua

--[[
   Save Table to File/Stringtable
   Load Table from File/Stringtable
   v 0.94
   
   Lua 5.1 compatible
   
   Userdata and indices of these are not saved
   Functions are saved via string.dump, so make sure it has no upvalues
   References are saved
   ----------------------------------------------------
   table.save( table [, filename] )
   
   Saves a table so it can be called via the table.load function again
   table must a object of type 'table'
   filename is optional, and may be a string representing a filename or true/1
   
   table.save( table )
      on success: returns a string representing the table (stringtable)
      (uses a string as buffer, ideal for smaller tables)
   table.save( table, true or 1 )
      on success: returns a string representing the table (stringtable)
      (uses io.tmpfile() as buffer, ideal for bigger tables)
   table.save( table, "filename" )
      on success: returns 1
      (saves the table to file "filename")
   on failure: returns as second argument an error msg
   ----------------------------------------------------
   table.load( filename or stringtable )
   
   Loads a table that has been saved via the table.save function
   
   on success: returns a previously saved table
   on failure: returns as second argument an error msg
   ----------------------------------------------------
   
   chillcode, http://lua-users.org/wiki/SaveTableToFile
   Licensed under the same terms as Lua itself.
]]--
do
   -- declare local variables
   --// exportstring( string )
   --// returns a "Lua" portable version of the string
   local function exportstring( s )
      s = string.format( "%q",s )
      -- to replace
      s = string.gsub( s,"\\\n","\\n" )
      s = string.gsub( s,"\r","\\r" )
      s = string.gsub( s,string.char(26),"\"..string.char(26)..\"" )
      return s
   end
--// The Save Function
function table.save(  tbl,filename )
   local charS,charE = "   ","\n"
   local file,err
   -- create a pseudo file that writes to a string and return the string
   if not filename then
      file =  { write = function( self,newstr ) self.str = self.str..newstr end, str = "" }
      charS,charE = "",""
   -- write table to tmpfile
   elseif filename == true or filename == 1 then
      charS,charE,file = "","",io.tmpfile()
   -- write table to file
   -- use io.open here rather than io.output, since in windows when clicking on a file opened with io.output will create an error
   else
      file,err = io.open( filename, "w" )
      if err then return _,err end
   end
   -- initiate variables for save procedure
   local tables,lookup = { tbl },{ [tbl] = 1 }
   file:write( "return {"..charE )
   for idx,t in ipairs( tables ) do
      if filename and filename ~= true and filename ~= 1 then
         file:write( "-- Table: {"..idx.."}"..charE )
      end
      file:write( "{"..charE )
      local thandled = {}
      for i,v in ipairs( t ) do
         thandled[i] = true
         -- escape functions and userdata
         if type( v ) ~= "userdata" then
            -- only handle value
            if type( v ) == "table" then
               if not lookup[v] then
                  table.insert( tables, v )
                  lookup[v] = #tables
               end
               file:write( charS.."{"..lookup[v].."},"..charE )
            elseif type( v ) == "function" then
               file:write( charS.."loadstring("..exportstring(string.dump( v )).."),"..charE )
            else
               local value =  ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
               file:write(  charS..value..","..charE )
            end
         end
      end
      for i,v in pairs( t ) do
         -- escape functions and userdata
         if (not thandled[i]) and type( v ) ~= "userdata" then
            -- handle index
            if type( i ) == "table" then
               if not lookup[i] then
                  table.insert( tables,i )
                  lookup[i] = #tables
               end
               file:write( charS.."[{"..lookup[i].."}]=" )
            else
               local index = ( type( i ) == "string" and "["..exportstring( i ).."]" ) or string.format( "[%d]",i )
               file:write( charS..index.."=" )
            end
            -- handle value
            if type( v ) == "table" then
               if not lookup[v] then
                  table.insert( tables,v )
                  lookup[v] = #tables
               end
               file:write( "{"..lookup[v].."},"..charE )
            elseif type( v ) == "function" then
               file:write( "loadstring("..exportstring(string.dump( v )).."),"..charE )
            else
               local value =  ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
               file:write( value..","..charE )
            end
         end
      end
      file:write( "},"..charE )
   end
   file:write( "}" )
   -- Return Values
   -- return stringtable from string
   if not filename then
      -- set marker for stringtable
      return file.str.."--|"
   -- return stringttable from file
   elseif filename == true or filename == 1 then
      file:seek ( "set" )
      -- no need to close file, it gets closed and removed automatically
      -- set marker for stringtable
      return file:read( "*a" ).."--|"
   -- close file and return 1
   else
      file:close()
      return 1
   end
end

--// The Load Function
function table.load( sfile )
   -- catch marker for stringtable
   if string.sub( sfile,-3,-1 ) == "--|" then
      tables,err = loadstring( sfile )
   else
      tables,err = loadfile( sfile )
   end
   if err then return _,err
   end
   tables = tables()
   for idx = 1,#tables do
      local tolinkv,tolinki = {},{}
      for i,v in pairs( tables[idx] ) do
         if type( v ) == "table" and tables[v[1]] then
            table.insert( tolinkv,{ i,tables[v[1]] } )
         end
         if type( i ) == "table" and tables[i[1]] then
            table.insert( tolinki,{ i,tables[i[1]] } )
         end
      end
      -- link values, first due to possible changes of indices
      for _,v in ipairs( tolinkv ) do
         tables[idx][v[1]] = v[2]
      end
      -- link indices
      for _,v in ipairs( tolinki ) do
         tables[idx][v[2]],tables[idx][v[1]] =  tables[idx][v[1]],nil
      end
   end
   return tables[1]
end
-- close do
end
Testcode
function Main()
     
dofile( "table.save-0.94.lua" )

print( "//Serialise Test ..." )
print( "//Building Table ..." )
	
local t = {}
t.a = 1
t.b = 2
t.c = {}
-- self reference
t.c.a = t
t.inass = { 1,2,3,4,5,6,7,8,9,10 }
t.inasst = { {1},{2},{3},{4},{5},{6},{7},{8},{9},{10} }
-- random
t.f = { [{ a = 5, b = 7, }] = "helloooooooo", [{ 1,2,3, m = 5, 5,6,7 }] = "A Table", }
t.func = function(x,y)
	print( "Hello\nWorld" )
	local sum = x+y
	return sum
end
-- get test string, not string.char(26)
local str = ""
for i = 0, 255 do
   --str = str.." "..i..": "..string.char( i )
   str = str..string.char( i )
end
t.lstri = { [str] = 1 }
t.lstrv = str

	
print( "\n## BEFORE SAVE ##" )

printtbl( t )

--// test save to file
assert( table.save( t, "test.tbl" ) == 1 )
	
--// test save to string
local strtbl = table.save( t )

--// test save to string over file
local strtbl2 = table.save( t, true )
	
-- load table from file
local t2 = table.load( "test.tbl" )

-- load table from string 1	
local t3 = table.load( strtbl )

-- load table from string 2
local t4 = table.load( strtbl2 )

print( "\n## AFTER SAVE ##" )

print( "\n## LOAD FROM FILE ##" )
printtbl( t2 )

print( "\n## LOAD FROM STRING ##" )
--printtbl( t3 )

print( "\n## LOAD FROM STRING OVER FILE ##" )
--printtbl( t4 )

print( "\n//Test References" )
	
assert( t2.c.a == t2 )
assert( t3.c.a == t3 )
assert( t4.c.a == t4 )
	
print( "\n//Test Long string" )

assert( t.lstrv == t2.lstrv )
assert( t.lstrv == t3.lstrv )
assert( t.lstrv == t4.lstrv )

print( "\n//Test Function\n\n" )

assert( t2.func(2,2) == 4 )
assert( t3.func(2,3) == 5 )
assert( t4.func(2,3) == 5 )

print( "\n*** Test SUCSESSFULL ***" )

end

function printtbl( t,tab,lookup )
	local lookup = lookup or { [t] = 1 }
	local tab = tab or ""
	for i,v in pairs( t ) do
		print( tab..tostring(i), v )
		if type(i) == "table" and not lookup[i] then
			lookup[i] = 1
			print( tab.."Table: i" )
			printtbl( i,tab.."\t",lookup )
		end
		if type(v) == "table" and not lookup[v] then
			lookup[v] = 1
			print( tab.."Table: v" )
			printtbl( v,tab.."\t",lookup )
		end
	end
end

Main()

See Also


FindPage · RecentChanges · preferences
edit · history
Last edited August 11, 2007 3:46 pm GMT (diff)