# Backtracking through a file

**URL:** <https://www.ruby-forum.com/t/backtracking-through-a-file/175395>\
**Category:** Ruby\
**Created:** [September 12, 2009, 5:57pm UTC](https://www.ruby-forum.com/t/backtracking-through-a-file/175395 "2009-09-12T17:57:51Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![atreides](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/ad7895/32.png) [@atreides](https://www.ruby-forum.com/u/atreides)\
**Post date:** [September 12, 2009, 5:57pm UTC](https://www.ruby-forum.com/t/backtracking-through-a-file/175395/1 "2009-09-12T17:57:51Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![atreides](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/ad7895/32.png) [@atreides](https://www.ruby-forum.com/u/atreides)\
**Post date:** [September 12, 2009, 6:10pm UTC](https://www.ruby-forum.com/t/backtracking-through-a-file/175395/2 "2009-09-12T18:10:44Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![atreides](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/ad7895/32.png) [@atreides](https://www.ruby-forum.com/u/atreides)\
**Post date:** [September 12, 2009, 9:27pm UTC](https://www.ruby-forum.com/t/backtracking-through-a-file/175395/3 "2009-09-12T21:27:47Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![atreides](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/ad7895/32.png) [@atreides](https://www.ruby-forum.com/u/atreides)\
**Post date:** [September 12, 2009, 9:30pm UTC](https://www.ruby-forum.com/t/backtracking-through-a-file/175395/4 "2009-09-12T21:30:53Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![atreides](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/ad7895/32.png) [@atreides](https://www.ruby-forum.com/u/atreides)\
**Post date:** [September 12, 2009, 9:44pm UTC](https://www.ruby-forum.com/t/backtracking-through-a-file/175395/5 "2009-09-12T21:44:12Z")

</div>

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
