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

A

Adam Bourg

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 Bourg 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

Attachments:
http://www.ruby-forum.com/attachment/4848/Screen_shot_2010-07-11_at_10.33.56_AM.png
 
S

Stefano Crocco

|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 Bourg 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
|

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
 
R

Rick DeNatale

if ( age.to_f.to_s =3D=3D 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 =3D=3D true
=A0monthsold =3D age.to_i * months.to_i
=A0puts '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 =3D~ /^d+$/ # for an integer
or
if age =3D~/^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 =3D Float(age) rescue nil
if float_age
#....

HTH

--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
D

Doc O'Leary

Adam Bourg said:
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.
 
A

Adam Bourg

Thanks Stefano. It has been a major help.

Also, thank you to others who've responded.
 
B

Brian Candler

Rick said:
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.
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.
=> 64
 
R

Rick DeNatale

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

oops
=3D> 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.

--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 

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
473,961
Messages
2,570,130
Members
46,689
Latest member
liammiller

Latest Threads

Top