Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to read a file "backwards" using Lua?

Tags:

lua

Rather than reading the file from the front, would it be possible to read it backwards? So that the output is from the back of the file to the front of the file.

EDIT: Last line displayed first, not fully backwards.

like image 271
fishy Avatar asked Feb 07 '23 17:02

fishy


2 Answers

It's possible, but cumbersome. Lua API provides seek function to set and get position in a file that read/write operations apply to.

So, you can use "seek" to read the file in small chunks from the end (for example, seek to filesize-1024 position, read 1024 bytes, find all end-of-lines, print the complete lines and store the leftover) and continue doing that going backward to the beginning of the file. The main advantage is that you shouldn't spend much more memory than the buffer you are reading (where as if you are reading from the beginning, but want to print in the reverse order, you'd need to have the entire file in memory), but it's likely to be slow.

like image 157
Paul Kulchenko Avatar answered Feb 09 '23 06:02

Paul Kulchenko


This solution is based on the idea of @PaulKulchenko.
Yes, it's cumbersome :-)

Define function io.linesbackward(filename) in the io library:

function io.linesbackward(filename)
  local file = assert(io.open(filename))
  local chunk_size = 4*1024
  local iterator = function() return "" end
  local tail = ""
  local chunk_index = math.ceil(file:seek"end" / chunk_size)
  return 
    function()
      while true do
        local lineEOL, line = iterator()
        if lineEOL ~= "" then 
          return line:reverse() 
        end
        repeat
          chunk_index = chunk_index - 1
          if chunk_index < 0 then 
            file:close()
            iterator = function() 
                         error('No more lines in file "'..filename..'"', 3) 
                       end  
            return 
          end
          file:seek("set", chunk_index * chunk_size)
          local chunk = file:read(chunk_size)
          local pattern = "^(.-"..(chunk_index > 0 and "\n" or "")..")(.*)"
          local new_tail, lines = chunk:match(pattern)
          iterator = lines and (lines..tail):reverse():gmatch"(\n?\r?([^\n]*))"
          tail = new_tail or chunk..tail
        until iterator
      end
    end
end

Usage:

local filename = "your_file.txt"
print("--- backward: ---------------------------")
for line in io.linesbackward(filename) do
  print(line)
end
print("--- forward: ----------------------------")
for line in io.lines(filename) do
  print(line)
end
print("-----------------------------------------")
like image 38
Egor Skriptunoff Avatar answered Feb 09 '23 08:02

Egor Skriptunoff