Basic Question: How do you check to see if gets is a number?

I wrote a basic script that asks for your age then converts it into
months, days and weeks old. I wanted to add some sort of control so that
it would return an error if you didn’t enter a number but rather a word
or a letter or something.

Can you please give me pointers? I’ve done quite a bit of Googling and
am not sure how to do this. When I enter a number or a letter it
returns the else statement. Please help! See attached for example of it
being run.

Here’s my script below:

Basic Program to ask for a persons age and out put that in months

Writen by Adam B. on 2010-07-11

Define verables

months = 12

Question that asks for your age

puts “Hello, in the field below please enter your age:”

Get’s is the ruby method that asks for your age, it gets the

information from # the terminal when you type
age = gets

This checks to see if you input a number, if you do it outputs the

results

if you don’t it asks you for it in a numerical format eg 21

if ( age.to_f.to_s == true )
monthsold = age.to_i * months.to_i
puts ‘You are ’ + monthsold.to_s + ’ months old’
else
puts ‘Error, please enter a number!’
end

On Sunday 11 July 2010, Adam B. wrote:

|Here’s my script below:
|# Get’s is the ruby method that asks for your age, it gets the
| puts ‘Error, please enter a number!’
| end
|

There are a couple of errors in your code. First of all, in ruby you
never
compare something with true (or false) in an if. That’s because the only
value
which is == to true is… true (and the only value which is == to false
is
false). This explains why you keep getting the error message: you’re
comparing
the string returned by the to_s method with true. These are different,
so the
== operator returns false, which leads to executing the “else” branch of
the
if.

The if statement in ruby works in the following way: it checks what
value the
condition evaluates to. If it’s the false object or the nil object,
then
the “else” branch is executed. All other values cause the “if” branch to
be
executed. So, to test whether a condition is true, you simply write:

if some_expression

else

end

where some_expression is any piece of ruby code which doesn’t evaluate
to
false or nil.

The first step to fix your code, then, is to remove everything left to
the
to_s inside the condition. You can also remove the brackets, as they’re
not
needed around the condition in an if statement.

This leaves us with:

if age.to_f.to_s

Now, the to_s method of any object will return a string which ruby will
always
consider a true value. This means that now we have the opposite problem
we had
before: the error message is never shown. Removing the to_s would help
if
String#to_f didn’t return 0.0 (which is also considered a true value) in
case
the string doesn’t represent a number.

The simplest way to tell whether a string represents a number is to use
the
Kernel#Float method, or Kernel#Integer if you want an integer number.
Unlike
String#to_f and String#to_i, which always return a number, these methods
raise
an exception if the string can’t be translated to a number. So you can
write:

#gets returns the string with a trailing newline, so you have to remove
it
age = gets.strip

age_numeric = begin Integer(age)
rescue ArgumentError
puts “Enter a valid number”
exit
end
#months is already an integer, so there’s no need to call to_i on it
monthsold = age_numeric * months

I hope this helps

Stefano

On Sun, Jul 11, 2010 at 12:34 PM, Adam B. [email protected]
wrote:

if ( age.to_f.to_s == true )

This will NEVER happen.

age.to_f converts the string to a float. If the string isn’t valid it
returns 0.0.

age.to_f.to_s will ALWAYS be a string, and will never == true

monthsold = age.to_i * months.to_i
puts ‘You are ’ + monthsold.to_s + ’ months old’
else

Now I’m not sure whether or not you really want to accept an age as a
float, or just an integer, but you need to do something else to
validate the string.

One way would be to use a regular expression, e.g. something like

if age =~ /^d+$/ # for an integer
or
if age =~/^d+(|.\d*)$/ # for a float

These are fairly strict in what they’ll accept, you might want
slightly different regular expressions, particularly if you really
want a float.

Or use either Kernel#Integer or Kernel#Float which raise an error if
they don’t like the string:

float_age = Float(age) rescue nil
if float_age
#…

HTH


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

In article [email protected],
Adam B. [email protected] wrote:

if ( age.to_f.to_s == true )

What do you expect this to evaluate to? Instead of “true”, did you mean
to use “age” (i.e., compare the original string with a reconverted
number string)? Even that test will fail:

irb(main):003:0> “42”.to_f.to_s == “42”
=> false

Barring full-blown input validation, the simple solution is to note that
non-numbers get converted to 0. That also happens to be an age you can
likely reject. Run with it.

Thanks Stefano. It has been a major help.

Also, thank you to others who’ve responded.

Stefano C. wrote:

age_numeric = begin Integer(age)
rescue ArgumentError
puts “Enter a valid number”
exit
end
#months is already an integer, so there’s no need to call to_i on it
monthsold = age_numeric * months

I hope this helps

Stefano

Rick Denatale wrote:

On Sun, Jul 11, 2010 at 12:34 PM, Adam B. [email protected]
wrote:

if ( age.to_f.to_s == true )

This will NEVER happen.

age.to_f converts the string to a float. If the string isn’t valid it
returns 0.0.

I’d use Float(age), which will raise an exception for invalid values.

irb(main):001:0> Float(“1”)
=> 1.0
irb(main):002:0> Float(“1.5”)
=> 1.5
irb(main):003:0> Float("1.5 ")
=> 1.5
irb(main):004:0> Float(“1.5 z”)
ArgumentError: invalid value for Float(): “1.5 z”
from (irb):4:in `Float’
from (irb):4
from :0

Now I’m not sure whether or not you really want to accept an age as a
float, or just an integer, but you need to do something else to
validate the string.

One way would be to use a regular expression, e.g. something like

if age =~ /^d+$/ # for an integer

except that particular regexp allows strings which are not just numbers,
e.g.

age = “foo\n123\nbar”
=> “foo\n123\nbar”
puts “oops” if age =~ /^\d+$/
oops
=> nil

Use \A and \z to match start and end of the string.

or
if age =~/^d+(|.\d*)$/ # for a float

except that particular regexp allows invalid floats (e.g. “1.”) and
disallows valid ones (e.g. “1.0e5”)

Using regexps to validate data is perfectly reasonable, just be very
careful that the regexp you write matches exactly what you expect and
nothing else.

Or use either Kernel#Integer or Kernel#Float which raise an error if
they don’t like the string:

float_age = Float(age) rescue nil
if float_age
#…

Beware that Integer() handles values which start with zero as octal.

“0100”.to_i
=> 100
Integer(“0100”)
=> 64

On Sun, Jul 11, 2010 at 4:53 PM, Brian C. [email protected]
wrote:

Use \A and \z to match start and end of the string.

In general you are right, but ignoring my typo leaving out the \ in
\d, in this case the string came from gets so the multi-line string
case won’t arise.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Hi –

On Mon, 12 Jul 2010, Rick DeNatale wrote:

=> nil

Use \A and \z to match start and end of the string.

In general you are right, but ignoring my typo leaving out the \ in
\d, in this case the string came from gets so the multi-line string
case won’t arise.

$/ = “r”
=> “r”
str = gets
foo
123
bar
=> “foo\n123\nbar”

Yes, yes, I know :slight_smile:

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Stay tuned for next event announcement!
Rubyist http://www.compleatrubyist.com