Backtracking through a file

M

M. Q.

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
 
R

Robert Klemme

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
 
7

7stud --

M. Q. said:
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
 
7

7stud --

7stud said:
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.
 
7

7stud --

7stud said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,169
Messages
2,570,919
Members
47,458
Latest member
Chris#

Latest Threads

Top