Backtracking through a file

IO.gets steps through a file one line at a time. However, I cannot
figure out how to (once i find something i need in the file) go
backwards line by line through that file until i find something else. I
thought possibly setting IO.lineno to the line before the current line
that would work, but I think I just misunderstood what lineno was for.
Anyone know a good way to do this?

Thanks

On 12.09.2009 17:57, M. Q. wrote:

IO.gets steps through a file one line at a time. However, I cannot
figure out how to (once i find something i need in the file) go
backwards line by line through that file until i find something else. I
thought possibly setting IO.lineno to the line before the current line
that would work, but I think I just misunderstood what lineno was for.
Anyone know a good way to do this?

One option is to create an Array of positions obtained via IO#tell while
you go along. Then you can move backwards to every line start.
Something like

File.open “foo” do |io|
idx = [io.tell]

while l = io.gets
p l
idx << io.tell
end
end

Cheers

robert

M. Q. wrote:

IO.gets steps through a file one line at a time.

As does IO.foreach. And IO.foreach’s block terminates naturally when
the end of the file is reached:

IO.foreach(“data.txt”) do |line|
puts line
end

I thought possibly setting IO.lineno to the line before the current
line that would work, but I think I just misunderstood what lineno
was for.

$ ri IO.lineno
-------------------------------------------------------------- IO#lineno
ios.lineno => integer

 Returns the current line number in _ios_. The stream must be opened
 for reading. +lineno+ counts the number of times +gets+ is called,
 rather than the number of newlines encountered. The two values will
 differ if +gets+ is called with a separator other than newline. See
 also the +$.+ variable.

    f = File.new("testfile")
    f.lineno   #=> 0
    f.gets     #=> "This is line one\n"
    f.lineno   #=> 1
    f.gets     #=> "This is line two\n"
    f.lineno   #=> 2

The key line in the description is:

lineno counts the number of times gets is called

It’s no different than if you wrote:

lineno = 0

IO.foreach(“data.txt”) do |line|
lineno += 1
puts “#{lineno}: #{line}”

if lineno==1
lineno = 30
end
end

data.txt:

hello
world
goodbye

–output:–
1: hello
31: world
32: goodbye

lineno is just a counter–it has no affect on the actual file pointer,
which marks the location of the current position in the file.

Anyone know a good way to do this?

prev_lines = []

IO.foreach(“data.txt”) do |line|
if line =~ /good/
prev_lines.reverse_each do |prev_line|
if prev_line =~ /hel/
puts prev_line
prev_lines = []
end
end
end

prev_lines << line
end

7stud – wrote:

M. Q. wrote:

IO.gets steps through a file one line at a time.

As does IO.foreach. And IO.foreach’s block terminates naturally when
the end of the file is reached:

IO.foreach(“data.txt”) do |line|
puts line
end

…and foreach closes the file for you automatically.

7stud – wrote:

Anyone know a good way to do this?

prev_lines = []

IO.foreach(“data.txt”) do |line|
if line =~ /good/
prev_lines.reverse_each do |prev_line|
if prev_line =~ /hel/
puts prev_line
prev_lines = []
end
end
end

prev_lines << line
end

Of course, if you’re files aren’t really big (e.g. 1+ GB), you can
simply read the whole file into memory:

lines = File.readlines(“data.txt”)

lines.each do |line|
if line =~ /good/
lines.reverse_each do |prev_line|
if prev_line =~ /hel/
puts prev_line
end
end
end
end