This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
The Higher Realm's profile picture

Saving multiple tables and loading them from a file

Started by The Higher Realm, 12 March 2017 - 12:59 AM
The Higher Realm #1
Posted 12 March 2017 - 01:59 AM
I feel like this should be simple, but I am struggling with it.
I can store a single table in a file with textutils and take it out but how would I go about storing multiple and be able to load one with a value from it and store into a variable.

So the file would contain something like this


{
  username="actiquack",
  password="secret password",
}
{
  username="dan",
  password="computercraft",
}

and I could grab the table with just a value like


function getTable(item, value)
  table = get entire table that has item == value
  table = textutils.unserialise(table)
  return table
end

data = getTable("username", "dan")

Then as an end result I would have a table that has the values so I could do data.password to get "computercraft"
Edited on 12 March 2017 - 01:00 AM
valithor #2
Posted 12 March 2017 - 02:20 AM
You could do something like this, assuming all of the tables written to the file are written using writeLine instead of just write:


local h = fs.open(file,"r") --# getting the information from the file that contains the tables
local str = h.readAll()
h.close()

local function unserializeTables()
  local returns = {} --# table to hold all the tables that will be returned
  local tbl --# creating a local variable named tbl
  while #str > 0 do
	tbl,str = str:match("^(.-\n}\n)(.*)") --# will explain below
	table.insert(returns,textutils.unserialize(tbl)) --# inserting the unserialized table into the returns table
  end
  return unpack(returns) --# returns the unserialized tables in order from the first found in the file
end

"^(.-\n}\n)(.*)"
– This is a lua pattern, which essentially looks for a closing curly brace "}" that is surrounded on both sides by a end line character \n. This only occurs at the last curly brace of a table, so you could unserialize nested tables using this.

A easier way to do this might be just to put all your tables into a single table before serializing it, say you wanted to save tables named tbl1, tbl2, and tbl3 to a file:


local saveTable = {tbl1,tbl2,tbl3}

local h = fs.open("saveFile","w")
h.write(textutils.serialize(saveTable))
h.close()

To read this file:

local h = fs.open("saveFile","r")
local tbl = textutils.unserialize(h.readAll())
h.close()

tbl1,tbl2,tbl3 = unpack(tbl)
Edited on 12 March 2017 - 01:29 AM
The Higher Realm #3
Posted 12 March 2017 - 03:07 AM

local h = fs.open(file,"r") --# getting the information from the file that contains the tables
local str = h.readAll()
h.close()

local function unserializeTables()
  local returns = {} --# table to hold all the tables that will be returned
  local tbl --# creating a local variable named tbl
  while #str > 0 do
	tbl,str = str:match("^(.-\n}\n)(.*)") --# will explain below
	table.insert(returns,textutils.unserialize(tbl)) --# inserting the unserialized table into the returns table
  end
  return unpack(returns) --# returns the unserialized tables in order from the first found in the file
end

textutils errors with: "textutils:300: attempt to concatenate string and nil"
tb1 variable doesn't get anything stored in it.
valithor #4
Posted 12 March 2017 - 03:12 AM

local h = fs.open(file,"r") --# getting the information from the file that contains the tables
local str = h.readAll()
h.close()

local function unserializeTables()
  local returns = {} --# table to hold all the tables that will be returned
  local tbl --# creating a local variable named tbl
  while #str > 0 do
	tbl,str = str:match("^(.-\n}\n)(.*)") --# will explain below
	table.insert(returns,textutils.unserialize(tbl)) --# inserting the unserialized table into the returns table
  end
  return unpack(returns) --# returns the unserialized tables in order from the first found in the file
end

textutils errors with: "textutils:300: attempt to concatenate string and nil"
tb1 variable doesn't get anything stored in it.

It works in my tests, make sure you are saving the tables to the file using writeLine instead of just write as I stated above the code.

edit:

writeLine puts endline characters after everything that is written, which is what the pattern is looking for.
Edited on 12 March 2017 - 02:13 AM
The Higher Realm #5
Posted 12 March 2017 - 03:21 AM

local h = fs.open(file,"r") --# getting the information from the file that contains the tables
local str = h.readAll()
h.close()

local function unserializeTables()
  local returns = {} --# table to hold all the tables that will be returned
  local tbl --# creating a local variable named tbl
  while #str > 0 do
	tbl,str = str:match("^(.-\n}\n)(.*)") --# will explain below
	table.insert(returns,textutils.unserialize(tbl)) --# inserting the unserialized table into the returns table
  end
  return unpack(returns) --# returns the unserialized tables in order from the first found in the file
end

textutils errors with: "textutils:300: attempt to concatenate string and nil"
tb1 variable doesn't get anything stored in it.

It works in my tests, make sure you are saving the tables to the file using writeLine instead of just write as I stated above the code.

edit:

writeLine puts endline characters after everything that is written, which is what the pattern is looking for.

I am my file looks like

{
  username="actiquack",
  pass="word",
}
{
  username="testname",
  pass="testpass",
}
I serialize the table and do h.writeLine(table)
Edited on 12 March 2017 - 02:22 AM
valithor #6
Posted 12 March 2017 - 03:29 AM
I have no idea why it is not working. I recreated the file using writeLine, and it worked as intended. It might just be easier to use the second solution.
The Higher Realm #7
Posted 12 March 2017 - 04:36 AM
I have no idea why it is not working. I recreated the file using writeLine, and it worked as intended. It might just be easier to use the second solution.

Alright I'm using the second option and I got it to work sorta. I'm trying to store usernames and passwords. You have a line like tb1, tb2, tb3 = uppack(tbl). How would I do that with any amount of tables and then be able to read through those and find the one with the matching username and password?
Bomb Bloke #8
Posted 12 March 2017 - 04:41 AM
I suggest a different table structure entirely:

{
	["user1"] = "password1",
	["user2"] = "password2",
	-- etc
}

A login check would look something like:

if users[userTryingToLogin] == read("*") then

If you wanted to store additional details for each user, you might do:

{
	["user1"] = {
			["password"] = "password1",
			["favouriteColour"] = colours.green,
			-- etc
		},
	
	["user2"] = {
			["password"] = "password2",
			["favouriteColour"] = colours.blue,
			-- etc
		},
	
	-- etc
}

A login check would look something like:

if users[userTryingToLogin].password == read("*") then
The Higher Realm #9
Posted 12 March 2017 - 05:22 AM
{
	["user1"] = {
			["password"] = "password1",
			["favouriteColour"] = colours.green,
			-- etc
		},
	
	["user2"] = {
			["password"] = "password2",
			["favouriteColour"] = colours.blue,
			-- etc
		},
	
	-- etc
}

This works better, but how do I write another table to the file?
Edited on 12 March 2017 - 04:50 AM
Bomb Bloke #10
Posted 12 March 2017 - 08:34 AM
Create one "master" table, stick your other tables into that, then serialise and write it to disk all in one go.

If you're having trouble seeing how that might work, give some examples of how you want the data to be organised when it's not in the file, but rather when it's loaded into RAM for your script to use.