File IO

Wondering if anyone has a version of lua io for Codea that supports append mode. Project I’m working on samples data every ten seconds, and I need to save it. Quickly runs into issues when I start trying to save thousands on records.

@Mark I’m not sure what info you’re saving, but is it possible to read your file into a table at the start, add you new info to the table, then at the end write the table to the file.

@dave1707 I do save the info into a table as it’s recorded, but I need to save it against the possibility if the user closing the program, battery failure, ios hiccup, or (surely not) a crash.

Here’s what I’ve used. It also supports reading and writing tables. It’s from a few code snippets from the forums and some mods.


--# Main
function setup()
    FileIO.write("test.txt","Hello World\
","a")
    str = FileIO.read("test.txt")
    print(str)
    
end


--# FileIO
--FileIO

FileIO = {}
local Table = {}
local path = os.getenv("HOME").."/Documents/"


--[[Document
--Function: FileIO.write(file,txt)
--Parameters:
--    file (string) File to save. ex. test.txt
--    txt (string) String to save to file.
--    m (string) Write mode. w - Write, a - append, r - read.
--Return:
--]]
FileIO.write = function(file,txt,m)
    mode = m or "r"
    
   local f = io.open(path..file,mode)
    assert(f ~= nil)
    if type(txt) == "table" then
      -- local str = "##FORMAT: table\
"..Table.tostring(txt) 
        local str = Table.tostring(txt) 
        f:write(str)
    else
        f:write(txt)
    end
    f:close()
    print("File saved as: "..file)
end


--[[Document
--Function: FileIO.read(file)
--Parameters:
--    file (string) File to read. ex. test.txt
--Return:
--    str (string)
--]]
FileIO.read = function(file)
    local f = assert(io.open(path..file,"r"))
    local t = f:read("*all")
    local res = Table.totable(t)
    f:close()
    if res then return res else return t end

end



--[[Document
--Function: FileIO.readB(file)
--Parameters:
--    file (string) File to save. ex. test.txt
--Return:
--    t (string)
--]]
FileIO.readB = function(file)
    local hBinaryFile = io.open(FileIO..file, "rb");
    local sBinaryContent = "";
    if(hBinaryFile)then
        while(true)do
            local block = hBinaryFile:read(512); -- Read only 512 bytes per iteration
            if(not block)then break;end
            sBinaryContent = sBinaryContent..block
            break
        end
        hBinaryFile:close();
    end
    local t = {}
    for i=1,sBinaryContent:len() do
        t[i] = sBinaryContent:byte(i)
    end
    return t
end


--[[Document
--Function: FileIO.writeB(file,txt)
--Description: Writes a binary file.
--Parameters:
--    file (string) File to save. ex. test.txt
--    sBinaryContent (string)
--Return:
--]]
FileIO.writeB = function(file,sBinaryContent)
    -- Now lets output it again.
    local hBinaryOutput = io.open(FileIO.path..file, "wb");
    if(hBinaryOutput)then
         hBinaryOutput:write(sBinaryContent); --You could do it in parts, but oh.
         hBinaryOutput:close();
    end
end

Table.val_to_str  = function( v )
  if "string" == type( v ) then
    v = string.gsub( v, "\
", "\\\
" )
    if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
      return "'" .. v .. "'"
    end
    return '"' .. string.gsub(v,'"', '\\\"' ) .. '"'
  else
    return "table" == type( v ) and Table.tostring( v ) or
      tostring( v )
  end
end

Table.key_to_str = function( k )
  if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
    return k
  else
    return "[" .. Table.val_to_str( k ) .. "]"
  end
end

Table.tostring = function( tbl )

  local result, done = {}, {}
  for k, v in ipairs( tbl ) do
    table.insert( result, Table.val_to_str( v ) )
    done[ k ] = true
  end
  for k, v in pairs( tbl ) do
    if not done[ k ] then
      table.insert( result,
        Table.key_to_str( k ) .. "=" .. Table.val_to_str( v ) )
    end
  end
  return "{" .. table.concat( result, "," ) .. "}"
end

Table.totable = function(self)
    
local t = loadstring("return "..self)

if t then
return assert(t(),"Can not convert string to table.")
end
    
end


The above code does not support append for tables, at least as in it wont be able to read it back. I’ll have to add that in.

@Mark The other thing you could do is write multiple files. If a file reaches a certain size, start writing to another file. When your done, combine the seperate files into a single one and remove the others. If there’s a problem like you describe, then when you restart the program, if there are multiple files, combine them on startup.

Found the solution by following the example of the file I/O in Log4Codea. Thanks, @brookesi.

If yours had easy storage / retrieval of tables, @Briarfox, it would be dead perfect.

@Mark Thanks for posting that. For some reason I missed the “a+” for appending to a file. Or it went into my short term memory like a lot of other stuff I forget.

@Mark The above code stores and retrieves tables very easily. It just can’t append to the file with a table. It needs to overwrite the file.

tbl = {"filed1",field2="Codea rocks"}
FileIO.write("test.tbl",tbl,"w")
tbl = FileIO.read("test.tbl")
print(tbl.field2)

@Briarfox just noodling around… How about a PersistedTable class. Init it with the file info, then use it as a frame in place of normal table function.

Something along the lines of…

pTable = PersistedTable("myfile.txt")
pTable.insert(myTable, aRecord)
pTable.remove(myTable, 2)

with the PersistedTable class both handling normal table functions, and persistent storage. Plus a load function to restore a complete table.

@Mark already working on it :slight_smile: