class Elif
A File-like object for reading lines from a disk file in reverse order. See ::new and #gets for details. All other methods are just interface conveniences.
Based on Perl's File::ReadBackwards module, by Uri Guttman.
Constants
- MAX_READ_SIZE
The size of the reads we will use to add to the line buffer.
- VERSION
The version of the installed library.
Public Class Methods
Works just line File::foreach, save that the lines come in reverse order.
# File lib/elif.rb, line 23 def self.foreach(name, sep_string = $/) open(name) do |file| while line = file.gets(sep_string) yield line end end end
The first half of the Elif algorithm (to read file
lines in reverse order). This creates a new Elif
object, shifts the read pointer to the end of the file, and prepares a
buffer to hold read lines until they can be returned. This method also sets
the @read_size
to the remainer of File#size and
MAX_READ_SIZE
for the first read.
Technically args
are delegated straight to File#new, but you
must open the File object for reading for it to work with this algorithm.
# File lib/elif.rb, line 63 def initialize(*args) # Delegate to File::new and move to the end of the file. @file = File.new(*args) @file.seek(0, IO::SEEK_END) # Record where we are. @current_pos = @file.pos # Get the size of the next of the first read, the dangling bit of the file. @read_size = @file.pos % MAX_READ_SIZE @read_size = MAX_READ_SIZE if @read_size.zero? # A buffer to hold lines read, but not yet returned. @line_buffer = Array.new end
Works just line File::open.
# File lib/elif.rb, line 32 def self.open(*args) file = new(*args) if block_given? begin yield file ensure file.close end else file end end
Works just line File::readlines, save that line Array will be in reverse order.
# File lib/elif.rb, line 49 def self.readlines(name, sep_string = $/) open(name) { |file| file.readlines(sep_string) } end
Public Instance Methods
Works just line File#close.
# File lib/elif.rb, line 151 def close @file.close end
Works just line File#each, save that the lines come in reverse order.
# File lib/elif.rb, line 125 def each(sep_string = $/) while line = gets(sep_string) yield line end end
The second half on the Elif algorthim (see ::new). This method returns the next line of the File, working from the end to the beginning in reverse line order.
It works by moving the file pointer backwords MAX_READ_SIZE
at
a time, storing seen lines in @line_buffer
. Once the buffer
contains at least two lines (ensuring we have seen on full line) or the
file pointer reaches the head of the File, the last line from the buffer
is returned.
When the buffer is exhausted, this will throw nil
(from the
empty Array).
# File lib/elif.rb, line 90 def gets(sep_string = $/) # # If we have more than one line in the buffer or we have reached the # beginning of the file, send the last line in the buffer to the caller. # (This may be +nil+, if the buffer has been exhausted.) # return @line_buffer.pop if @line_buffer.size > 2 or @current_pos.zero? # # If we made it this far, we need to read more data to try and find the # beginning of a line or the beginning of the file. Move the file pointer # back a step, to give us new bytes to read. # @current_pos -= @read_size @file.seek(@current_pos, IO::SEEK_SET) # # Read more bytes and prepend them to the first (likely partial) line in the # buffer. # @line_buffer[0] = "#{@file.read(@read_size)}#{@line_buffer[0]}" @read_size = MAX_READ_SIZE # Set a size for the next read. # # Divide the first line of the buffer based on +sep_string+ and #flatten! # those new lines into the buffer. # @line_buffer[0] = @line_buffer[0].scan(/.*?#{Regexp.escape(sep_string)}|.+/) @line_buffer.flatten! # We have move data now, so try again to read a line... gets(sep_string) end
Works just line File#readline, save that the lines come in reverse order.
# File lib/elif.rb, line 134 def readline(sep_string = $/) gets(sep_string) || raise(EOFError, "end of file reached") end
Works just line File#readlines, save that line Array will be in reverse order.
# File lib/elif.rb, line 142 def readlines(sep_string = $/) lines = Array.new while line = gets(sep_string) lines << line end lines end