iblue95
iblue95

Reputation: 31

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

Upvotes: 1

Views: 401

Answers (2)

the Tin Man
the Tin Man

Reputation: 160551

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.

Upvotes: 1

Olkin
Olkin

Reputation: 181

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

Upvotes: 0

Related Questions