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

[HTTP] read all into a table?

Started by TyDoesMC024, 11 October 2016 - 08:13 PM
TyDoesMC024 #1
Posted 11 October 2016 - 10:14 PM
I do not know how to explain this but I want to save a group of variables per line.

something like this

TyDoesMC024:300:600:true:false

That does not represent anything I am actually using, I honestly don't know where to start.
but this will be like users info and its saved on a php site that my program would be fetching (string.find)
But it needs to fetch the right var for the right person. Is this possible?
':' represents a new var BTW.

Forgot to mention. Every user would have its own line in the file.
supernicejohn #2
Posted 11 October 2016 - 10:46 PM
(Not tested as on mobile, just pseudocode)
for i=1,( length of file) do
   if line:sub(1,line:find(':')) == user then
      return line:sub(line:find(':'),#line)
   end
end
Where 'line' is a line in the file and 'user' is the username you want, and a similar approach can be used to get any variable in the string (using select() for example)

If that is not what you were asking for then I apologize. Hope it helps though!
InDieTasten #3
Posted 11 October 2016 - 11:02 PM
You could also consider saving and delivering this as a serialized lua table instead of colon delimited lines. then you'd just textutils.unserialize(responseBody) and have a table ready to go.
If you have to use this format, consider looking into string matching. supernicejohns submission is not complete.
Sewbacca #4
Posted 12 October 2016 - 08:08 AM

readLines = function(sPath)
  local File = fs.open(sPath)
  return function()
    local var = File.readLine()
    if not var then
      File.close()
      return nil
    end
    return var
  end
end

users = {}
for line in readLines(<path>) do -- try for line in io.lines(<path>) do
  local bFirst = true
  local sUser;
  for var in line:gmatch '[^:]' do
    if bFirst then
      users[var] = {}
      sUser = var
      bFirst = false
    else
      users[sUser][#users[sUser] + 1] = var
    end
  end
end
reutrn users

Should work.
hbomb79 #5
Posted 12 October 2016 - 10:38 AM
Here is some code I whipped up. It will get all the values after the username and insert them into the users table.

local str, results = "TyDoesMC024:300:600:true:false", {} --# Store the string we are parsing in 'str' and an empty table in 'results'
--# In the real use case, you may have a table of strings you would iterate over for each user. This is just an example

--# First things first, what user are we dealing with here? This line will find all characters infront of the first semicolon.
--# This example assumes at least one semicolon will be present after the user name
local userName = str:match "^([^:]+):"
results[ userName ] = {} --# Create an empty table where our values will go in the next step

--# Next, we parse each value. To do this, we match each value inbetween the semicolons following the username.
--# To skip the username, we simply prefix the `gmatch` call with a semicolon which means it will only start the search after the first semicolon

for val in str:gmatch ":(/>[^:]+)" do
	--# You could do anything here. For this example, I simply inserted the value into the users table
	table.insert( results[ userName ], val )
end

--# Here is the output of this example:
print( "User '"..userName.."' has config of: " .. textutils.serialise( results[ userName ] ) )
As you can see, without comments explaining the code - it is reasonably short. I am sure it could be shorter, but that may reduce readability

The forum editor is being silly and adding a `/>` in the `str:gmatch` above. Remove that before testing/using the code, else it will fail.

Referring to this

for val in str:gmatch ":(/>[^:]+)" do --# This line is being messed up, should not be /> in there
	--# You could do anything here. For this example, I simply inserted the value into the users table
	table.insert( results[ userName ], val )
end

It sees the semicolon and bracket as an emoticon I think, and for some reason inserts those characters.
Edited on 12 October 2016 - 08:39 AM
hbomb79 #6
Posted 12 October 2016 - 10:47 AM
If you were reading over each line in an http return, you may use `httpReturnValue.readLine()`, and use that as `str`. You would put that in a loop so that `str` changes to the new line each time.

You would also need to move `results` outside of that loop, something like


local results = {} --# move results outside of loop
local h = http.get "http://example.com" --# perform request
local str = h.readLine() --# get first line
while str and str ~= "" do --# while a line exists keep going (not sure if it returns nil or "" when no next line. Used both for this example).
	--# The main code
	str = h.readLine() --# get the next line. If there is not one, the loop will break (exit)
end
h.close()
--# All done
Edited on 12 October 2016 - 08:47 AM
houseofkraft #7
Posted 12 October 2016 - 01:12 PM
You could do this:


local tablename = {}
local website = "http://example.com"
local req = http.get(website)
local reqbefore = req -- For making the counter be able to reset
if not req then
  error("Request Failed!")
end
local lines = 0
while req.readLine() do
		lines = lines + 1
end
req.close()
local req = reqbefore
local counter = 0
while not counter == lines do
		local line = req.readLine()
		table.insert(tablename, line)
end
req.close()
Edited on 12 October 2016 - 11:13 AM
Bomb Bloke #8
Posted 12 October 2016 - 02:52 PM
You could do this:

That won't work the way you're thinking - when you assign a table to a variable (such as the ones http.get() returns), you're really assigning a pointer to a table. When you copy "req" to "reqbefore", it's not the table that gets copied, but rather it's the pointer; you end up with two variables leading to the one table.

Eg:

local a = {}
local b = a

b[2] = "World"

print("Hello "..a[2])  --> "Hello World"

This line is also busted:

while not counter == lines do

Putting aside that you forgot to increment counter, Lua will read that as while (not counter) == lines do; "not counter" will resolve as false, which will never equal "lines" and so the loop won't repeat.

There's also no need to iterate through the handle twice - you could do as hbomb did to get it done within a single loop, perhaps even use the readLine function as an actual iterator function in order to simplify it further. Something like that which Sewbacca was aiming for:

local request = http.get("http://example.com")

for line in request.readLine do
   -- Parse line here
end

req.close()

Frankly I reckon InDieTasten has it right, though - simply using textutils.serialise() / textutils.unserialise() seems like the way to go here.

It sees the semicolon and bracket as an emoticon I think, and for some reason inserts those characters.

Indeed. Using the full post editor to disable emoticons is the workaround.
Edited on 12 October 2016 - 12:57 PM