Search and match string, only after a certain point in a file - ruby

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

Related

How to test for stdout that is inside a .each iteration block, using rspec?

I have a sample code:
def print_stuff(first_num, second_num)
puts 'hello'
(first_num..second_num).to_a.each do |num|
puts 'The current number is: '
puts "#{num}"
end
end
I and using rspec, I would like to test to see if the output is correct or not. My attempt are as follows:
expect(initialize_program(1, 3)).to output(
"The current number is:
1
The current number is:
2
The current number is:
3").to_stdout
But instead, I get a expected block to output to stdout but not a block error since the initialize_program(1,3) is outputting the texts, but it is done inside a .each block thus the method itself returns the array of range of numbers.
How can I test for the texts inside the block, to see if the outputted texts are correct?
Thanks!
Todd's answer is fine and I'd strongly recommend you read it carefully: refactor your app in a way that UI (CLI in your case) is minimal and easy to test. But when you want full coverage you'd need to test that std output eventually.
The way you're using it now:
expect(initialize_program(1, 3)).to output("whatever").to_stdout
Means that initialize_program(1, 3) is evaluated immediately when the matcher is called, and it's too soon - it has to do it's magic(*) first, and then run your code. Try like this:
expect { initialize_program(1, 3) }.to output("whatever").to_stdout
Now, instead passing results of calling initialize_program(1, 3) into the matcher, you pass a block that "knows how" to call initialize_program(1, 3). So what the matcher does:
saves the block for later
does it magic to capture whatever goes to the STDOUT (see below)
calls the block, calling the initialize_program(1, 3), capturing whatever was supposed to go to STDOUT
compares it with what you've set up in your expectation (the output("whatever") part)
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher
The mentioned magic is not that magical anyway:
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L49
and
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L141
It just creates StringIO, and replaces global var $stdout with it.
Refactor to Inspect a String, Not Standard Output
This type of code is why you should write your tests first. You're essentially testing Kernel#puts, which always returns nil, rather than validating that you've constructed the String you expect. Don't do that. Instead, refactor like so:
def print_stuff(num1, num2)
str =
(num1..num2).map { |num|"The current number is: #{num}" }
.join "\n"
puts str
str
end
print_stuff 1, 3
#=> "The current number is: 1\nThe current number is: 2\nThe current number is: 3"
This will not only print what you expect on standard output:
The current number is: 1
The current number is: 2
The current number is: 3
but will also use the implicit return of the last line of your method to return a value that you can use to compare to the expectations in your spec.
You might also refactor the method to return an Array of String objects, or whatever else you might explicitly want to test. The more your real method reflects what you plan to test, the better.
RSpec Examples
RSpec.describe '#print_stuff' do
it 'prints the expected message' do
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
expect(print_stuff 1, 3).to eql(expected_string.chomp)
end
# Even without the collection matchers removed in RSpec 3,
# you can still validate the number of items returned.
it 'returns the expected number of lines' do
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)
end
end
Testing RSpec Examples in IRB
In irb, you can validate your specs like so:
require 'rspec'
include RSpec::Matchers
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
# String#chomp is needed to strip the newline from the
# here-document
expect(print_stuff 1, 3).to eql(expected_string.chomp)
# test the returned object in other ways, if you want
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)

No output produced

Can anyone tell me why this program is not producing an output? The output it should be producing is: Line read: 0
Line read: 1 Line read: 2 Line read: 3 and so on.
So far, I am not getting an output even though I have fixed a number of bugs. Any help or suggestions would be much appreciated.
# takes a number and writes that number to a file then on each line
# increments from zero to the number passed
def write(aFile, number)
# You might need to fix this next line:
aFile.puts(number)
index = 0
while (index < number)
aFile.puts(index.to_s)
index += 1
end
end
# Read the data from the file and print out each line
def read(aFile)
# Defensive programming:
count = aFile.gets
if (is_numeric?(count))
count = count.to_i
index = 0
while (index < count)
line = aFile.gets
puts "line read: " + line
index+=1
end
end
end
# Write data to a file then read it in and print it out
def main
aFile = File.new("mydata.txt", "w") # open for writing
write(aFile, 10)
aFile.close
aFile = File.new("mydata.txt", "r")
read(aFile)
aFile.close
end
# returns true if a string contains only digits
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
main
Your code isn't written in the Ruby way.
This is how I'd write it if I wanted to closely mimic your code's logic:
# takes a number and writes that number to a file then on each line
# increments from zero to the number passed
def write_data(fname, counter)
File.open(fname, 'w') do |fo|
fo.puts(counter)
counter.times do |n|
fo.puts n
end
end
end
# returns true if a string contains only digits
def is_numeric?(obj)
obj[/^\d+$/]
end
# Read the data from the file and print out each line
def read_data(fname)
File.open(fname) do |fi|
counter = fi.gets.chomp
if is_numeric?(counter)
counter.to_i.times do |n|
line_in = fi.gets
puts 'Line read: %s' % line_in
end
end
end
end
# Write data to a file then read it in and print it out
DATA_FILE = 'mydata.txt'
write_data(DATA_FILE, 10)
read_data(DATA_FILE)
Which outputs:
Line read: 0
Line read: 1
Line read: 2
Line read: 3
Line read: 4
Line read: 5
Line read: 6
Line read: 7
Line read: 8
Line read: 9
Notice these things:
Method (or variable) names are not in camelCase in Ruby, they're snake_case. ItsAReadabiltyThing.
Ruby encourages us to use a block when opening files for reading or writing, to automatically close the file when we're finished with it. Leaving danging file handles opened then not closed, in a loop, in a long-running program, is a great way for your program to crash in a way that's hard to figure out. SO has many questions that resulted from doing that. This is from the IO#open documentation:
With no associated block, ::open is a synonym for ::new. If the optional code block is given, it will be passed io as an argument, and the IO object will automatically be closed when the block terminates. In this instance, ::open returns the value of the block.
Usually you'll see code use File.open instead of IO.open, mostly out of habit in Ruby coders. File inherits from IO and adds some additional file-oriented methods to the class, so it's a little more full-featured.
Ruby has many methods that help us avoid using while loops. Getting the counters wrong or missing a condition that should terminate the loop, is all too common in programming, so Ruby makes it easy to loop "n times" or to iterate over all the elements in an array. The times method accomplishes that nicely.
String's [] method is really powerful and makes it easy to look at the contents of a string and apply a pattern or a slice. Using /^\d+$/ checks the entire string to make sure all characters are digits, so some_string[/^\d+$/] is a shorter version than what you're doing and accomplishes the same thing, returns a "truthy" value.
We don't use a main method. That's old-school Pascal, C or Java and is artificially structured. Ruby's a little more friendly than that.
Instead of using
3.times do |n|
puts n
end
# >> 0
# >> 1
# >> 2
I'd probably use
puts (0..(3 - 1)).to_a * "\n"
# >> 0
# >> 1
# >> 2
just because I tend to think in Perl terms. It's another old habit.
I found 2 errors. Fixing those errors gives you desired output.
Error #1.
Your method is_numeric? always returns false. Even if your condition is true. The last line of the method is false and therefore the whole method ALWAYS returns false.
You can fix it in 2 steps.
Step #1:
if /[^0-9]/.match(obj) == nil
true
else
false
end
It's not a good practice to return booleans within conditional. You can simplify it this way:
def is_numeric?(obj)
/[^0-9]/.match(obj) == nil
end
or even better
def is_numeric?(obj)
/[^0-9]/.match(obj).nil?
end
Error #2 is inside your read method. If you try to output the value of count after you read it from the file it gives you "10\n". That \n at the end messes you up.
To get rid of \n when you read from the file you could possibly use chomp. So then your reading line would be:
count = aFile.gets.chomp
and the rest works like magic

Callback after every `each` iteration when `next` is called

I want to run some code after every iteration of each. Is there a way to do this without repeating the code? I tried this:
(1..10).each do |n|
continue = Proc.new {
puts "ended #{n}"
next
}
continue.call if n == 2
puts n
continue.call
end
but it didn't work.
In my actual code, I have lots of next calls. That's why it's unpractical to call a method every time I call next.
Approach 1
Define the contents of the loop in a method that you call from the loop. You can put an ensure block in the method. That way, your method can use return anywhere you want to move onto the next iteration, but you still guarantee you execute the ensure code:
def doit(x)
return if x == 2
puts "I like the number #{x}"
ensure
puts "LOOP"
end
[1,2,3,4].each{|x| doit(x)}
results in
I like the number 1
LOOP
LOOP
I like the number 3
LOOP
I like the number 4
LOOP
Approach 2
Similar to approach 1, but allows you reuse the "callback" code for different concerns. It also keeps you using next instead of return. This is to define a method that yields and then does other stuff:
def ensure_LOOP(x)
yield
puts "LOOP"
end
[1,2,3,4].each do |x|
ensure_LOOP(x) do
next if x == 2
puts "I really like the number #{x}"
end
end
Results in
I really like the number 1
LOOP
LOOP
I really like the number 3
LOOP
I really like the number 4
LOOP
And
[1,2,3,4].each do |x|
ensure_LOOP(x) do
next unless x == 2
puts "I don't like the number #{x}"
end
end
results in
LOOP
I don't like the number 2
LOOP
LOOP
LOOP
As I understand the question, you don't want anything executed after continue.call if n==2. If that's correct, you could use the control expression next with an argument.
def m(n)
puts "Only #{n} more days!"
end
(1..6).each do |n|
next m(n) if n==3
puts n
m(n)
end
1
Only 1 more days!
2
Only 2 more days!
Only 3 more days!
4
Only 4 more days!
5
Only 5 more days!
6
Only 6 more days!
Just call it within the each loop?
(1..10).each do |n|
puts n
puts "ended #{n}"
end
The code you provided actually does run, and outputs the following:
1
ended 1
ended 2
2
ended 2
3
ended 3
4
ended 4
5
ended 5
6
ended 6
7
ended 7
8
ended 8
9
ended 9
10
ended 10
As you can see, the Proc gets called twice for the number 2, as your if condition passes in that case and calls the Proc.
Stepping back, defining a method outside the context of the iteration is probably a better choice. It will make the code easier to read and avoid redefining the Proc each time the each block executes.
That said, technically what you have here seems to run okay.

How can I get line feeds in Ruby when writing out to a file:

My input file is:
1
3
5
7
9
I want my output file to be squares, one per line:
1
9
25
49
81
But I am getting:
19254981
i.e. no linespaces
My code is:
a= File.open('inputs')
b= File.open('outputs', 'w')
a.each_line do |one_num|
one_number = one_num.to_i
square= one_number * one_number
b << square
end
use puts instead of <<.
b.puts square
Side note: you can do the whole thing as one long method chain:
File.open('outputs','w').puts(File.open('inputs').readlines.map{ |l| n=l.to_i; n*n })
Or somewhat more readably as nested blocks:
File.open('outputs','w') do |out|
File.open('inputs') do |in|
out.puts( in.readlines.map { |l| n=l.to_i; n*n } )
end
end
Both of those solutions have the advantage of not leaving any dangling file handles despite the lack of explicit close statements.

Equivalent of "continue" in Ruby

In C and many other languages, there is a continue keyword that, when used inside of a loop, jumps to the next iteration of the loop. Is there any equivalent of this continue keyword in Ruby?
Yes, it's called next.
for i in 0..5
if i < 2
next
end
puts "Value of local variable is #{i}"
end
This outputs the following:
Value of local variable is 2
Value of local variable is 3
Value of local variable is 4
Value of local variable is 5
=> 0..5
next
also, look at redo which redoes the current iteration.
Writing Ian Purton's answer in a slightly more idiomatic way:
(1..5).each do |x|
next if x < 2
puts x
end
Prints:
2
3
4
5
Inside for-loops and iterator methods like each and map the next keyword in ruby will have the effect of jumping to the next iteration of the loop (same as continue in C).
However what it actually does is just to return from the current block. So you can use it with any method that takes a block - even if it has nothing to do with iteration.
Ruby has two other loop/iteration control keywords: redo and retry.
Read more about them, and the difference between them, at Ruby QuickTips.
I think it is called next.
Use next, it will bypass that condition and rest of the code will work.
Below i have provided the Full script and out put
class TestBreak
puts " Enter the nmber"
no= gets.to_i
for i in 1..no
if(i==5)
next
else
puts i
end
end
end
obj=TestBreak.new()
Output:
Enter the nmber
10
1
2
3
4
6
7
8
9
10
Use may use next conditionally
before = 0
"0;1;2;3".split(";").each.with_index do |now, i|
next if i < 1
puts "before it was #{before}, now it is #{now}"
before = now
end
output:
before it was 0, now it is 1
before it was 1, now it is 2
before it was 2, now it is 3

Resources