fileluafopen

File handling in lua


I am trying to write something to a file using a function then read it using another function. Here is my code:-

local getContent = function(fName)
  local file = io.open(fName, "r")

  if file == nil then
    print("Could not open the file")
    return
  end

  local fData = file:read("*all")
  print("File data:-\n" .. fData)
  file:close()
end

local writeToFile = function(fName, fWrite)
  local file = io.open(fName, "r+")

  if file == nil then
    print("Could not open the file")
    return
  end

  print("Content: " .. file:read("*all")) -- When I use r+ and read the file it acts as append mode and adds the file to the next line
  -- If I am not reading the file then it acts as write mode and truncates the file
  file:write(fWrite)
  -- When I try to read file here then it throws an error
  file:close()
end

local fileName = "lua-file.txt"
writeToFile(fileName, "My name is Dante\n")
getContent(fileName)

Q1. When I try to read the file in modes with "+" at end like "r+" and after writing to them inside the writeToFile() then the terminal throws an error. I can read the file before writing to it. How can I solve this?

Q2. "w+" and "a+" behave as I expect them to. "w+" truncates the file before writing and "a+" appends data to it at the last line. "r+" depends if am reading the file before writing to it then it appends the data if I am not reading before writing then it truncates the file before writing.

Q3. When I am not reading the file before writing i.e. remove line 8 from writeToFile() function print("Content: " .. file:read("*a")) then it truncates the data as I said above but if I change to append+ mode run the file then change back to read+ then it doesn't write to the file at all.


Solution

  • To write and read to and from an open file:

    Whenever you successfully perform any read or write operation on a file - regardless of the mode the file was opened with - the file position indicator for the file will have changed.

    For r+ (read extended), the file position indicator is initially set to the start of the file, and both reading and writing are allowed1. Starting by reading the entire file then places the indicator at the end of the file. Immediately following this with a write operation will then appear as an append, and the file position indicator will still be at the end of the file. Attempting to read at this point will not return any data from the file - you must first seek to a position with data.

    This can be done with file:seek. For example,

    file:seek('set')
    

    places the file position indicator at the start of the file.

    1. Note that because Lua's I/O is a very thin wrapper around C's I/O, we should refer to documentation on fopen to learn the order in which operations may be performed on a file opened for both reading (input) and writing (output):

    In update mode ('+'), both input and output may be performed, but output cannot be followed by input without an intervening call to fflush, fseek, fsetpos or rewind, and input cannot be followed by output without an intervening call to fseek, fsetpos or rewind, unless the input operation encountered end of file. In update mode, implementations are permitted to use binary mode even when text mode is specified.

    From this we can see that a call to file:write immediately followed by a call file:read is not actually allowed. You must call file:seek (or file:flush) in-between writing and reading.


    If you want to open your file a second time, in order to read what you have just written, you must first commit these changes to the file with file:flush (or file:close), as otherwise the data is surely buffered (see also file:setvbuf).


    Aside: you describe immediately writing to a file after opening it with r+ mode as having truncated the file, but what is actually happening is the new data is overwriting the previous data. Only when the new data is at least equal in length to the previous data does it appear as though truncation occurred, otherwise the 'tail' of the previous data will remain.