Page 1 of 2

Storage - Save data to files

Posted: Thu Sep 27, 2012 5:26 pm
by Anickyan
I made this little utility, which allows you to easily save data to files.

Data types supported:
- Strings
- Functions --- VIA STRINGS ONLY
- Numbers
- Booleans
- Tables

Data types NOT supported:
- Userdata

Functions that can be used:

Code: Select all

storage.save(path, value) -- path:string, value:string/boolean/number/table | saves <value> to <path>
storage.load(path) -- path:string | loads and returns a value(string/boolean/number/table/function) from <path>
storage.readTable(t) -- t:table | returns a string containing the table
This utility also allows you to use the flib utility, which I wrote some time ago. This allows you to do some easy file manipulation.
These are the functions in flib(taken from another post, so not too detailed)

Code: Select all

flib.exists(path) --Returns true if path exists, and is a file
flib.getTable(path) --Returns a table with all the lines
flib.getLine(path, n) --Returns the Nth line
flib.getText(path) --Returns all the text in the file
flib.fwrite(path, text) --Writes text in a file
flib.fappend(path, text) --Appends text to a file
flib.fwriteAtStart(path, text) --Writes text at the start of a file
flib.fwriteFromTable(path, table) --Writes each field in table as a line in the file
flib.fappendFromTable(path, table) --Appends to a file with each field in table as a line
flib.fwriteAtStartFromTable(path, table) --Writes at the start of a file with each field in table as a line
flib.fwriteFromLine(path, n, text) --Writes text from Nth line, and keeps everything beneath the Nth line
flib.fwriteFromLineFromTable(path, line, table) --Writes from Nth line with each field in table as a line, and keeps everything beneath the Nth line

flib.replaceLine(path, line, text) --Replaces the Nth line in a file with text
flib.getName(path) --Returns the name of the file, without the path
flib.getPath(path) --Returns the path of the file, without the name
flib.fremove(path) --Removes a file
flib.getFiles(path) -- Returns a table with all of the files in path/. REQUIRES LÖVE
flib.isDir(path) -- Returns true if path is a directory, false otherwise. REQUIRES LÖVE
-----------------------------------------------------------------------------------------

Code(just copy into a file, and name it anything):

Code: Select all

flib = {}

function flib.exists(path)
	if love then
		return love.filesystem.exists(path)
	else
		local file = io.open(path, "r")
		if file ~= nil then
			file:close()
			return true
		end
		
		return false
	end
end

--[[
function flib.exists(path)
	if love then
		return love.filesystem.exists(path)
	else
		print "user pls"
		os.exit()
	end
end
]]

function flib.getTable(path)
	if exists(path) then
		local file = io.open(path, "r")
		local lines = {}
		local i = 1
		local line = file:read("*l")
		while line ~= nil do
			lines[i] = line
			line = file:read("*l")
			i = i + 1
		end
		file:close()
		return lines
	end
	return {}
end

function flib.getLine(path, n)
	if exists(path) then
		local lines = getTable(path)
		return lines[n]
	end
	return ""
end

function flib.getText(path)
	if exists(path) then
		local file = assert(io.open(path, "r"))
		return file:read("*a")
	end
	return ""
end

function flib.fappend(path, text)
	local file = assert(io.open(path, "a"))
	file:write(text.."\n")
	file:close()
end 

function flib.fwrite(path, text)
	local file = assert(io.open(path, "w"))
	file:write(text)
	file:close()
end

function flib.fwriteAtStart(path, text)
	local _text = getText(path)
	fwrite(path, text.."\n".._text)
end

function flib.fwriteFromTable(path, t)
	local text = ""
	for _, line in pairs(t) do
		text = text..line.."\n"
	end
	fwrite(path, text)
end

function flib.fappendFromTable(path, t)
	local text = ""
	for _, line in pairs(t) do
		text = text..line.."\n"
	end
	fappend(path, text)
end 

function flib.fwriteAtStartFromTable(path, t)
	local text = ""
	for _, line in pairs(t) do
		text = text..line.."\n"
	end
	fwriteAtStart(path, text)
end

function flib.fwriteFromLine(path, n, text)
	if exists(path) then
		local lines = getTable(path)
		local file = io.open(path, "w")
		local count = 0
		
		for i = 1, n do
			file:write(lines[i].."\n")
			count = count + 1
		end
		
		file:write(text.."\n")
		
		for i = n + 1, #lines + count do
			if lines[i] ~= nil then
				file:write(lines[i].."\n")
			end
		end
		
		file:close()
	end
end

function flib.fwriteFromLineFromTable(path, n, _lines)
	if exists(path) then
		local lines = getTable(path)
		local file = io.open(path, "w")
		local count = 0
		
		for i = 1, n do
			file:write(lines[i].."\n")
			count = count + 1
		end
		
		for _, line in pairs(_lines) do
			file:write(tostring(line).."\n")
		end
		
		for i = n + 1, #lines + count do
			if lines[i] ~= nil then
				file:write(lines[i].."\n")
			end
		end
		
		file:close()
	end
end

function flib.replaceLine(path, n, text)
	local lines = getTable(path)
	lines[n] = text
	fwriteFromTable(path, lines)
end

function flib.getName(path)
	if exists(path) then
		local lastSlashPos = 1
		for i = 1, path:len() do
			if path:sub(i, i) == "/" then
				lastSlashPos = i
			end
		end
		
		return path:sub(lastSlashPos + 1)
	end
	return ""
end

function flib.getPath(path)
	if exists(path) then
		local lastSlashPos = 1
		for i = 1, path:len() do
			if path:sub(i, i) == "/" then
				lastSlashPos = i
			end
		end
		
		return path:sub(1, lastSlashPos)
	end
	return ""
end

function flib.fremove(path)
	if os.remove then
		os.remove(path)
	else
		fs.remove(path)
	end
end

function flib.getFiles(path)
	if love then
		return love.filesystem.enumerate(path)
	else
		print "user pls"
		os.exit()
	end
end

function flib.isDir(path)
	if love then
		return love.filesystem.isDirectory(path)
	else
		print "user pls"
		os.exit()
	end
end

storage = {}

local function readTable(t)
	local s = "{"

	for k, v in pairs(t) do
		if type(k) == "string" then
			s = s .. tostring(k) .. " = "
		end

		if type(v) == "string" then
			s = s .. "\"" .. tostring(v) .. "\"" .. ", " 
		elseif type(v) == "number" or type(v) == "boolean" then
			s = s .. tostring(v) .. ", "
		elseif type(v) == "table" then
			s = s .. readTable(v) .. ", "
		end
	end

	s = s .. "\"end\"}"
	return s
end

function storage.save(path, value)
	if type(value) == "number" or type(value) == "boolean" then
		local s = tostring(value)
		flib.fwrite(path, "return " .. s)
	elseif type(value) == "string" then
		local s = tostring(value)
		s = "\"" .. s .. "\""
		flib.fwrite(path, "return " .. s)
	elseif type(value) == "table" then
		local s = readTable(value)
		flib.fwrite(path, "return " .. s)
	end
end

function storage.load(path)
	if not storage.exists(path) then
		error "Bad argument #1 to 'storage.new()': file does not exist"
	end

	return dofile(path)
end

function storage.readTable(t)
	return readTable(t)
end

Re: Storage - Save data to files

Posted: Thu Sep 27, 2012 8:58 pm
by qaisjp
you can store functions by using string.dump(fn), this returns a binary version of the function which is stored in a string and can be put in files.

Re: Storage - Save data to files

Posted: Thu Sep 27, 2012 9:22 pm
by Roland_Yonaba
qaisjp wrote:you can store functions by using string.dump(fn), this returns a binary version of the function which is stored in a string and can be put in files.
Just works for functions without upvalues, though.

Re: Storage - Save data to files

Posted: Thu Sep 27, 2012 9:25 pm
by Nixola
What does upvalue mean?

EDIT: Karma to you, Qais

Re: Storage - Save data to files

Posted: Thu Sep 27, 2012 10:36 pm
by Roland_Yonaba
I suck at explaining technical terms, but i'll rather give a self-explanatory example.
Upvalues can be considered as 'external local variables', in a closure.
Say that you want to make a counter:

Code: Select all

count = 0

function counter ()
  count = count + 1
end 
counter() -- count is 1
counter() -- count is 2
Problem is the variable count is global here, therefore can be accessed by any other function.
So, we can enclose it inside the scope of a function, and return another one who will still have access to it.

Code: Select all

function new_counter ()
  local count = 0
  -- returns a function which accesses to local count
  return function ()
    count = count + 1
   print('count is', count)
  end
end

counter = new_counter ()
counter() -- count is 1
counter() -- count is 2

To the point of view of counter, variable count is neither local nor global. It is an upvalue.

Re: Storage - Save data to files

Posted: Thu Sep 27, 2012 11:56 pm
by slime
qaisjp wrote:you can store functions by using string.dump(fn), this returns a binary version of the function which is stored in a string and can be put in files.
string.dump is not portable between architectures (except when using LuaJIT), Lua versions besides revisions, or between Lua and LuaJIT.

Re: Storage - Save data to files

Posted: Fri Sep 28, 2012 10:16 am
by Anickyan
And that's the reason, that I didn't use string.dump :)

Anyway, do you like it?

Re: Storage - Save data to files --permission denied

Posted: Mon Jun 23, 2014 6:29 am
by DarthGrover
Thanks for this library. I think it really going to help me a lot. But when I tried:

storage.save("myfile.txt", mytable)
I get an error saying permission denied
myfile is in the root folder i tried it as .lua and .txt

I think I am missing a basic concept here.

Re: Storage - Save data to files

Posted: Mon Jun 23, 2014 1:01 pm
by Robin
For some reason this library mixes love.filesystem with Lua io, which doesn't work. I suggest using something else.

Re: Storage - Save data to files

Posted: Mon Jun 23, 2014 2:47 pm
by DarthGrover
thanks. can you suggest a different library?