How do I do scan a file after finding a particular line. The first results do not count. For example (search for 3s after finding a line that says "begin here"):
Sample file
1
3
3
4
begin here
2
3
3
1
4
The expected output would be two 3s
How do I do scan a file after finding a particular line. The first results do not count. For example (search for 3s after finding a line that says "begin here"):
Sample file
1
3
3
4
begin here
2
3
3
1
4
The expected output would be two 3s
Ruby's ..
operator is your friend:
DATA.each_line do |line|
line.chomp!
next unless (line =~ /^begin here$/) .. false
puts line if line == '3'
end
__END__
1
3
3
4
begin here
2
3
3
1
4
Save that to a file and run it and you'll see:
3
3
..
(and the very much more obscure ...
) come from Perl, and are very useful for just this particular kind of use.
What happens is ..
is a two-state operator that works with if
or unless
to look for a first condition which is line =~ /^begin here$/
in this case. When that condition is met, the operator sticks, i.e., returns true until the second test matches, (returns true). Once that happens, the flip-flop then starts returning false again.
In this code, I'm fooling it into always returning true, because what would be the second test is always false
. In other words, the code then reads to the end of the data.
..
(and ...
) are REALLY useful when you're scanning a file, looking for blocks of data that occur throughout the file. If the second conditional test found the end of the block, ..
would reset and the code would skip through the file until it found the next block start, triggered, and started capturing again.
It's also likely that most people haven't seen __END__
or DATA
used. __END__
is a way to tell Ruby that there is no additional code to be executed beyond that point. DATA
is an internal file handle that points to the lines following __END__
and can be treated similarly to IO and File objects. It's really useful for supplying data you must have with a script, but don't necessarily want to put into a separate file. In this case I'm pretending that the lines after __END__
are a separate file. Easy-peasy.
I would write it like this.
Code
def put3s(fname)
e = IO.foreach(FName)
until e.next.strip == "begin here"; end
loop { puts "3" if e.next.strip == "3" }
end
Note that IO#foreach without a block returns an enumerator. foreach
is your best friend.
After the last element of the enumerator has been reached, e.next
raises a StopIteration
exception. Kernel#loop handles the exception by breaking out of the loop.
Example
We first create a file:
text =<<_
1
3
3
4
begin here
2
3
3
1
4
_
FName = "t"
File.write(FName, text)
and then execute the method
put3s(FName)
# 3
# 3