I can convert an object to an integer with to_i. How can I get it to not convert if the input is not a number?
In python I would do this:
h=input("Number: ")
try:
h=int(h)
except ValueError:
print("Please enter numbers!")
So I tried this in ruby:
print "Number: "
h=gets.chomp
try(h.to_i)
print(h)
end
but it prints the input even if I enter letters, so this means I am doing it wrong. What is the correct Ruby way of doing this?
Reading ruby docs for to_i
Returns the result of interpreting leading characters in str as an
integer base base (between 2 and 36). Extraneous characters past the
end of a valid number are ignored. If there is not a valid number at
the start of str, 0 is returned. This method never raises an exception
when base is valid.
What you want in your case (to map the Python behavior) is:
begin
Integer(gets)
rescue ArgumentError
puts "Please enter numbers!"
end
Related
I'm trying to loop this question until the user's input is a string value:
Question: What is your name?
I don't want the user to just press enter and leave the name blank.
I don't want the user's input to be numeric/numbers.
Please see my code below:
name1 = gets.chomp.to_s
loop do
print "Please enter your name "
name1 = gets.chomp.to_s
if name1.empty?
puts "No input."
else name1.to_i
puts "Illegal character ':'"
end
end
With this code, I can't proceed to the next question even if I input a string value. Please help.
Your code has several issues:
Your input and output is out of order. You gather input before prompting and that input (from your first line) is never used:
name1 = gets.chomp.to_s # <- Ruby is waiting for input
loop do
print "Please enter your name " # <- user is prompted to enter name
name1 = gets.chomp.to_s # <- previous input is overwritten
# ...
end
The first line should probably be deleted.
gets might return nil, but chomp always returns a string. Calling to_s afterwards is therefore superfluous.
Your if-else construct is actually:
if name1.empty?
puts "No input."
else
name1.to_i
puts "Illegal character ':'"
end
So whenever the input is not empty?, you convert it to an integer (discarding the result) and print an error message. You probably want an elsif instead (/.../ is a regexp and \d matches a digit):
if name1.empty?
puts 'No input.'
elsif name1.match? /\d/
puts 'Digits not allowed.'
end
You could also use a case expression:
case name1
when ''
puts 'No input.'
when /\d/
puts 'Digits not allowed.'
end
You never break out of your loop. The code keeps looping even if no error was found. This can be fixed by adding a break statement in an else branch (to either if or case):
# ...
else
break
end
gets.chomp will always return a string, and as such there is no need to call to_s on the method.
If you don't want the user to be able to input any integers, you could use the following for a clean solution:
name.count("0-9") > 0
If this returns true, then you know that the user's input contains at least one number.
I need to check whether my variable is an Integer or a String.
The code below will just break the loop, without warning me for an illegal character. Can anyone help me to find the mistake?
x = 0
while x == 0
name = gets.chomp.capitalize
if name.empty?
puts "No input. Try again"
elsif name.is_a? Integer
puts "Illegal character: Integer "
else
x = 1
end
end
Because gets returns a string you need to find out if the string represents a number (and only a number).
First, translate your string to an integer with to_i. Please note that to_i returns 0 for strings that do not include numbers. In a second step check if translating this integer back into a string matches the original string
string.to_i.to_s == string
Note that this is just a simple example, it wouldn't work for example with the string 00.
Another way might be checking if the string only contains numbers. That could be done by using a regexp:
string.match(/\A\d+\z/)
You can do something like this:
loop do
puts "Enter name"
name = gets.chomp
if name.empty?
puts "No input, try again"
elsif name.scan(/\d+/).any?
puts "Illegal character: Integer"
else
raise StopIteration
end
end
case-expression
Or use a case-expression to tidy things up.
loop do
puts "Enter name"
case gets.chomp
when ''
puts "No input, try again"
when /\d/
puts "Illegal character: Integer"
else
raise StopIteration
end
end
See String#scan, Array#any? and StopIteration for further details
I need to check whether my variable is an Integer or a String.
The code below will just break the loop, without warning me for an illegal character. Can anyone help me to find the mistake?
x = 0
while x == 0
name = gets.chomp.capitalize
if name.empty?
puts "No input. Try again"
elsif name.is_a? Integer
puts "Illegal character: Integer "
else
x = 1
end
end
Because gets returns a string you need to find out if the string represents a number (and only a number).
First, translate your string to an integer with to_i. Please note that to_i returns 0 for strings that do not include numbers. In a second step check if translating this integer back into a string matches the original string
string.to_i.to_s == string
Note that this is just a simple example, it wouldn't work for example with the string 00.
Another way might be checking if the string only contains numbers. That could be done by using a regexp:
string.match(/\A\d+\z/)
You can do something like this:
loop do
puts "Enter name"
name = gets.chomp
if name.empty?
puts "No input, try again"
elsif name.scan(/\d+/).any?
puts "Illegal character: Integer"
else
raise StopIteration
end
end
case-expression
Or use a case-expression to tidy things up.
loop do
puts "Enter name"
case gets.chomp
when ''
puts "No input, try again"
when /\d/
puts "Illegal character: Integer"
else
raise StopIteration
end
end
See String#scan, Array#any? and StopIteration for further details
I'm trying to catch the difference between a numeric string vs. an arbitrary string:
'0'.to_f
#=> 0.0
'hello'.to_f
#=> 0.0
Both of the above return a Float. How do I catch the difference if the user inputs the actual value '0' or if the user inputs the value 'hello'?
I am trying to create a simple Celsius to Fahrenheit calculator. If the user inputs "hello" the program should output Please type in a number: but if the user types in 0 then the program should output the correct Fahrenheit calculation.
Use this:
number = Float( string_to_convert ) rescue nil
if number.nil?
puts "#{string_to_convert} is not a number"
else
# DO the conversion
end
This will use a sensible set of rules for converting String values into Floats, supporting negative numbers, scientific notation, whilst not requiring you write a regular expression to try and capture all the valid ways of expressing floating point numbers in Ruby.
The rescue is required to catch the error from a failed conversion.
Potentially better Ruby code for your particular purpose (and combining design from Tamer's answer with feedback from Stefan in comments):
begin
number = Float( string_to_convert )
rescue ArgumentError
puts "'#{string_to_convert}' is not a number"
else
# Do the conversion, using number variable
end
However, if the program flow is more complicated than input-or-bust-then-repeat, I still find the one-liner can be useful - provided of course you either raise an error later or can deal with missing values because the conversion to a Float failed.
You could use a regular expression like:
/
\A # anchor on start of a string, so there there is nothing more than float allowed
\-? # optional minus
\d+ # non-empty row of digits
(
\. # dot
\d+ # another row of digits
)? # ? means both of the above are optional
\z # anchor on end of a string, so there there is nothing more than float allowed
/x
single line version: /\A\-?\d+(\.\d+)?\z/
In typical use-case, Float( ) might be better, but using a regex separates you from Ruby's definition of Float literal, that can come handy if i.e. you want to allow a comma as decimal mark, or thousand separator (Ruby will only allow . and _ respectively). Regex match will also just return true or false, that can be useful if you want to avoid exception handling - ArgumentError thrown by Float( ) on fail is pretty generic, so it might get thrown by other nearby method call, and thus can get difficult to handle properly, or just make your code ugly.
begin
value = Float(input)
# do your correct logic here
rescue ArgumentError
puts "Please type in a number!"
end
As I understand it, the way to accept user input is
puts "Can you guess what number the computer is thinking of?"
userguess = gets.chomp
gets.chomp is a string method and so if the user enters the number 5, userguess stores the value "5" as string. I would then have to do userguess.to_i! to convert this to an int. However, I would not like to do this. I want to accept the user input either as a string or an int and then have the program do something like:
if #guess.is_a?(Integer) == true
puts "I got your number. Let me get back to you."
# do something
elsif #guess.is_a?(Integer) == false
puts "That's not a number. You MUST enter a number! Try again"
# Ask the user to guess again.
else
#something else
end
I don't want to accept the user input explicitly as a string because I want to check if it is a string or an int later on in the program. How would I do this?
That is impossible. All user input from the terminal are a string. If it were possible, how would you think a user can input a number 5 as opposed to a string "5"?
No, that is not possible.
But you can define a little function to check whether that string can be an integer or not:
def is_i?(s)
s.to_i.to_s == s
end
Be aware that string with spaces will not be an integer in this case:
is_i? '123' # true
is_i? '123 ' # false
is_i? ' 123' # false
is_i? '12 123' # false
To handle second and third example you can strip your user input.
Your code will look like:
guess = gets.chomp.strip
if is_i? guess
puts 'is integer'
else
puts 'is not an integer'
end
To check if a string contains valid integer (or float, etc.) you could use this approach:
def coerce(string)
Integer(string) rescue Float(string) rescue string
end
coerce('115').class # Fixnum
coerce('115.12').class # Float
coerce('115.aa').class # String
Also check out highline gem, it provides lots of helpful functionality when it comes to cli.
You don't understand how a keyboard device and the console input work.
ALL input typed on the keyboard and read via gets or anything else, is always a String. You can not get anything else.
We use gets.chomp to remove the trailing newline that is entered when the user presses Return or Enter. For instance, a bare gets will return a line-end if I enter nothing else:
gets #=> "\n"
Adding additional characters results in those characters, plus the terminating line-end:
gets #=> "foo\n"
We use chomp to remove that trailing line-end. Repeating the same inputs and using chomp:
gets.chomp #=> ""
gets.chomp #=> "foo"
Ruby makes it easy to tell if what was input can be cleanly converted to an integer. Simply use Integer(). Here is some output from an IRB session:
>> Integer('1') #=> 1
>> Integer('123') #=> 123
>> Integer('foo')
ArgumentError: invalid value for Integer(): "foo"
from (irb):14:in `Integer'
from (irb):14
from /usr/local/bin/irb:12:in `<main>'
>> Integer('1a')
ArgumentError: invalid value for Integer(): "1a"
from (irb):15:in `Integer'
from (irb):15
from /usr/local/bin/irb:12:in `<main>'
Using a rescue makes it easy to handle the exception:
begin
Integer('1')
rescue ArgumentError
puts 'not a number'
end #=> 1
Or:
begin
Integer('a')
rescue ArgumentError
'not a number'
end #=> "not a number"