in article gMmv6.3969$pS2.193227 / typhoon.mn.mediaone.net, Daniel Berger at
djberg96 / hotmail.com wrote on 3/25/01 5:54 AM:
>
> I *do not* want to read the entire file into memory
>
Dan,
The following should do what you want (and handle at least some of the
pathologic conditions (e.g. linelength > buffersize)
Regards, Paul
# irb -r rev --prompt xmp
r = ReverseFile.new File.new '/var/log/messages'
==>#<ReverseFile io=#<File:0x81e46f4>>
r.reverseReadline
==>"Mar 25 21:37:18 norbert dhclient: DHCPREQUEST on ex0 to 192.168.1.1
port 67"
r.reverseReadline
==>"Mar 25 21:12:16 norbert dhclient: bound to 192.168.1.104 -- renewal
in 1502 seconds."
count = 0
==>0
r.reverseReadlines { count += 1 }
==>nil
count
==>257
===== rev.rb =====
class ReverseFile
def initialize(io,readback=8000)
raise RuntimeError, "readback must be >1" unless readback > 1
@io = io
@readback = readback
@fpos = @io.stat.size
@linebuf = readbuf
return self
end
def reverseReadline
@linebuf = readbuf if @linebuf == []
if block_given?
yield @linebuf.pop
else
return @linebuf.pop
end
end
def reverseReadlines
lins = []
while (line=reverseReadline) != nil
if block_given?
yield line
else
lines << line
end
end
return lines unless block_given?
end
def inspect
"#<ReverseFile io=#{@io}>"
end
def readbuf
seekpos = (@fpos-@readback)>0 ? (@fpos-@readback) : 0
@io.seek seekpos
newfpos = @io.pos
lines = @io.read(@fpos-newfpos).split $/
if newfpos == 0
@fpos = 0
return lines
elsif lines.length <= 1
@readback = @readback * 2
return readbuf
else
firstline = lines.shift
@fpos = newfpos + firstline.length
return lines
end
end
private :readbuf
end