The Crystal Programming Language Forum

Reading a file from the end to the beginning line by line

I have a large file with alot of lines.
But now I want to read the file from the endline towards the beginning line, this line by line.
How do I do this ?

This is very difficult, and unfortunately seems like nobody has implemented a library.
But there’s this tac command that’s typically on Linux, which I think reverse streams a file, so it could be used.

Process.run("tac", ["foo.txt"]) do |tac|
  tac.output.each_line do |line|
    ...
  end
end

The following worked :

Blockquote
Process.run(“rev”, ["/var/log/messages"]) do |r|
r.output.each_line do |line|
puts line.reverse
end
end

Then you can use directly String#reverse.

A not the most efficient, simple Crystal native implementation:

str = File.read "./README.md"

while true
  str, _, last_line = str.rpartition '\n'
  break if str.empty? && last_line.empty?
  puts last_line
end

@j8r OP wants to avoid reading the file into memory because it’s huge

Ok, sure, can be read by chunks, something like this?:

bytesize = 32
last_string = ""
break_next = false

File.open "./README.md" do |file|
  offset = file.size.to_i - bytesize

  while true
    file.read_at offset, bytesize do |io|
      string = io.gets_to_end + last_string
      while true
        string, _, last_line = string.rpartition '\n'
        if string.empty?
          last_string = last_line
          break
        end
        puts last_line
      end
    end
    offset -= bytesize

    if break_next
      break
    elsif offset <= 0
      bytesize += offset
      offset = 0
      break_next = true
    end
  end

  puts last_string
end
1 Like

You can skip to EOF and read lines backwards, essentially IO#gets with inverse direction.

This is a old library from the Ruby world for doing operations to do just that, so it might be of some use to you:

Not sure if it would translate to Crystal properly, but something to consider.

1 Like