lenso_here
lenso_here

Reputation: 110

User input string or integer

I'm trying to find out if a user inputs a string or an integer. I can find it out if I enter a fixed value with user input's requirement. I can't get its class.

This works:

name = 5 
type = name.class

This doesn't work:

print "Enter the value to findout its class "
name = gets
type = name.class
if type == String
  puts "this is a string"
elsif type == Fixnum
  puts "this is a fixnum"
else
  puts "this is unknown type"
end

Upvotes: 2

Views: 4582

Answers (5)

Rahul Patel
Rahul Patel

Reputation: 1424

(gets.chomp == '0' || gets.chomp.to_i != 0) => true for input is integer

Upvotes: 0

Prabhakar
Prabhakar

Reputation: 6754

How about this?

class String
  def is_number?
    true if Float(self) rescue false
  end
end

Now call is_number? on user_input

user_input.is_number?

Upvotes: 0

Stefan
Stefan

Reputation: 114158

You could call Kernel#String, Kernel#Integer and Kernel#Float to find "valid" classes, i.e. classes you can convert the input into.

def valid_classes(value)
  classes = []
  classes << String(value) rescue nil
  classes << Integer(value) rescue nil
  classes << Float(value) rescue nil
  classes.map(&:class)
end

valid_classes("foo")                   #=> [String]
valid_classes("123.456")               #=> [String, Float]
valid_classes("123")                   #=> [String, Fixnum, Float]
valid_classes("100000000000000000000") #=> [String, Bignum, Float]

Other notations are recognized, too:

valid_classes("1_000")                 #=> [String, Fixnum]
valid_classes("1e20")                  #=> [String, Float]
valid_classes("0b111")                 #=> [String, Fixnum]

If you need more control, use a regular expression.

Here's a simple REPL:

loop do
  print "Enter a value to find out its class: "
  input = gets.chomp
  puts "'#{input}' could be a: #{valid_classes(input).join(', ')}"
end

Upvotes: 2

user2536065
user2536065

Reputation:

I think the question I would have to ask you is how is the user expected to enter a string?

If the user enters:

10 #=> Fixnum
1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000 #=> Bignum
1.0 #=> Float
"hello" #=> String
'hello' #=> String

Then you can just do:

def test_input
  input = gets.chomp
  puts eval(input).class
rescue NameError
  puts "Unknown Input Class"
end

This actually should work with all defined objects, so if you only want to catch those four that you mentioned then:

def test_input
  input = gets.chomp
  klass = eval(input).class
  raise NameError unless [Fixnum, Bignum, Float, String].include?(klass)
  puts klass
rescue NameError
  puts "Unknown Input Class"
end

EDIT:

To circumvent the use of eval with direct user input, which is a vulnerability, Gumbo suggests to use a parser instead.

$ gem install parser 

#!/usr/bin/ruby
require 'parser/current'

def test_input
  input = Parser::CurrentRuby.parse(gets.chomp)

  puts case input.type
  when :str then "String"
  when :float then "Float"
  when :int 
    eval(input.to_sexp.slice(/\d+/)).class 
    # Yes using eval again to get Fixnum vs Bignum, but this time 
    # we know what the input will be, and that's ok to eval
  else
    "Unknown Input Class"
  end
end

Upvotes: 2

Abdo
Abdo

Reputation: 14051

As @ArupRakshit mentioned in his comment above, gets always gets you a String.

What you want to do is get the user's input and determine what it is.

For instance, "1" is a String but 1 is a Fixnum.

[2] pry(main)> "1".class
=> String
[3] pry(main)> 1.class
=> Fixnum

In your comment above, you mentioned that gets.to_i gives you an integer. There's a problem with that. String#to_i returns 0 for strings that are not numeric:

[6] pry(main)> gets.to_i
hello
=> 0

[7] pry(main)> "hello".to_i
=> 0

So basically you're getting a String and will be determining its possible class(es).

# add more Kernel methods here
POSSIBLE_CLASSES = [Integer, Float, Bignum, String]

str = gets.chomp # chomp gets rid of the \n that gets appended 
                 # to your String because you press enter

# As @Stefan does it, but with meta-programming
POSSIBLE_CLASSES.collect { |p| 
  (Kernel.method(p.name).call(str); p) rescue nil 
}.compact

Explanation of the last line:

Kernel.Integer("1") returns 1 Kernel.Float("1.0") returns 1.0

So basically, I want to call a Kernel's method which name is the same as my class.

Kernel.method( method_name ) returns the method to me, which I then call with the string str.

Kernel.Integer("hello") would throw an ArgumentError; which will be rescued and a nil will be collected.

So basically, the line above will loop through the possible classes and attempt initializing them with the string we got from the console. We collect the class if there's no exception and nil otherwise. Then we compact the array (remove the nils), which will then contain the 'valid' classes.

Note that our code above only supports Kernel types and can be easily tweaked to support other classes.

Upvotes: 1

Related Questions