Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lua: When and how to write tables to _G

I am learning Lua from a book, and I am NOT a programmer. I am trying to save a table of data to a file using the following functions (that were copied directly from the book), but the function is getting an error when trying to get a string from _G[resTable]. Why?

function readFromFile(filename,resTable)
    local hfile = io.open(filename)
    if hfile == nil then return end
    local results = {} -why is this table here?
    local a = 1
    for line in hfile:lines() do-- debug shows this loop doesn't run (no lines in hfile?)
        _G[resTable[a]] = line
        a = a + 1
    end
end

function writeToFile(filename, resTable)
    local hfile = io.open(filename, "w")
    if hfile == nil then return end
    local i
    for i=1, #resTable do
        hfile:write(_G[resTable[i]])--bad argument #1 to 'write' (string expected, got nil)
    end
end

'writeToFile" gets an error when trying to :write to _G[resTable[i]]. In the two previous functions listed here, I don't understand why they are referencing _G[resTable[i]] since I don't see any code that is writing to _G.

So here is the order of execution:

local aryTable = {
"Score",
"Lives",
"Health",
}

readFromFile("datafile", aryTable)

writeToFile("datafile", aryTable)

and I get an error:

bad argument #1 to 'write' (string expected, got nil)
stack traceback:
[C]: in function 'write'
test.lua:45: in function 'writeToFile'
test.lua:82: in main chunk
like image 243
PHazer Avatar asked Oct 10 '13 19:10

PHazer


People also ask

What does _G mean in Lua?

A global variable (not a function) that holds the global environment (see §2.2). Lua itself does not use this variable; changing its value does not affect any environment, nor vice versa.

How do I set global variables in Lua?

The syntax for declaring a global variable in Lua is pretty straightforward, just declare whatever name you want to use for your variable and assign a value to it. It should be noted that we cannot declare a global variable without assigning any value to it, as it is not something that Lua allows us to do.

How do you create a table in Lua?

In Lua the table is created by {} as table = {}, which create an empty table or with elements to create non-empty table. After creating a table an element can be add as key-value pair as table1[key]= “value”.

Do you need to declare variables in Lua?

In lua, there is no need to declare or initialize variables before they are used. By default, variables are initialized to the value of nil.


1 Answers

Apparently the author has implemented a way of saving a list of global variables to file and restore them.

The function writeToFile expects a filename and a list of global variables names (resTable). Then it opens a the filename for writing and iterates over the provided names:

for i=1, #resTable do
    hfile:write(_G[resTable[i]])
end

in this loop resTable[i] is the i-th name and _G[resTable[i]] is the corresponding value, taken from the table _G, which stores all the globals. If a global with that name is not defined, _G[resTable[i]] will return nil, which is the cause of the failure you experienced. Thus you must provide a resTable that is filled with names of existing globals to avoid this error.

Apart from this, the serialization strategy of the author is really naive, since it handles only variables with string values. In fact by saving the variables to file like that the type information is lost, thus a variable having the value "100" (a string) and another with value 100 (a number) will be stored the same on disk.

The problem is evident analyzing the readFromFile function. After opening the file for reading, it scans it line by line, creating a new variable for each name mentioned in its resTable list:

local a = 1
for line in hfile:lines() do
    _G[resTable[a]] = line
    a = a + 1
end

the problem is manyfold:

  • the loop variable line will always have a string value, thus the recreated globals will be all strings, even if they were numbers originally;
  • it assumes that the variables are recreated in the same order, thus you must provide the same names in resTable you used when you saved the file;
  • it assumes that the values are stored one per line, but this is a false assumption, since the writeToFile function doesn't write a newline character after each value;

Moreover that local results = {} is useless and in both functions the file handle hfile is not closed. This latter is very bad practice: it could waste system resources and if your script fails part of the supposedly written data could never make its way to disk, since it may be still stuck in some buffer. File handles are automatically closed when the script ends, but only if it ends in a sane way.

Unless you did some error in pasting the code or omitted significant parts of it or the book is building some example incrementally, I dare say it is fairly crappy.


If you want a quick and dirty way to save and retrieve some globals you could use this:

function writeToFile( filename, resTable )
    local hfile = io.open(filename, "w")
    if hfile == nil then return end
    for _, name in ipairs( resTable ) do
        local value = _G[name]
        if value ~= nil then
            hfile:write( name, " = ")
            local vtype = type( value )
            if vtype == 'string' then
                hfile:write( string.format( "%q", value ) )
            elseif vtype == 'number' or vtype == 'boolean' then
                hfile:write( tostring( value ) )
            else
                -- do nothing - unsupported type
            end
            hfile:write( "\n" )
        end
    end
    hfile:close()
end

readFromFile = dofile

It saves the globals as a Lua script and reads them back by executing the script using Lua dofile function. Its main limitation is that it can only save strings, booleans an numbers, but usually this is enough while learning.

You can test it with the following statements:

a = 10
b = "20"
c = "hello"
d = true
print( a, b, c, d )
writeToFile( "datafile", { "a", "b", "c", "d" } )
a, b, c, d = nil
print( a, b, c, d )
readFromFile( "datafile" )
print( a, b, c, d )

If you need more advanced serialization techniques you can refer to Lua WIKI page on table serialization.

like image 133
Lorenzo Donati -- Codidact.com Avatar answered Sep 19 '22 05:09

Lorenzo Donati -- Codidact.com