2

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
5
  • 6
    #gets always gives you the string...BTW Commented May 2, 2014 at 7:45
  • thats true. when i enter gets.to_i it gives always integer. So what is method to get what is entered? Commented May 2, 2014 at 7:48
  • That depends what are the types you want to valid... Commented May 2, 2014 at 7:52
  • if you want to see if the string is a number, have a look here: stackoverflow.com/questions/5661466/… Commented May 2, 2014 at 7:53
  • i want to get if the input is a fixnum, bignum, string or float Commented May 2, 2014 at 7:53

5 Answers 5

2

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
Sign up to request clarification or add additional context in comments.

6 Comments

eval? Are you kidding me?
eval does not evaluate just literals but also any Ruby code.
yeah I realize that. it depends on how the user input is accepted. but it seems the best way to get the desired outcome. with conversions, even kernel conversions like Kernel#Integer ect. you have to make a choice of which to convert first or do as Stefan does in his answer and create a list of valid classes, but that doesn't seem to be the intended result and look, it converts any numeric input to a float, so...
You could better use a Ruby parser, which doesn’t execute the given code.
still ended up using eval to get Fixnum and Bignum because the parser just cares that it is an int
|
2

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

5 Comments

Great use of Kernel#Integer ; I reused your stuff in my answer =) +1
hi Stefan, I just started learning ruby 15 days ago. I already learned methods and classes. however i'm still not enough experienced to enter your answer into my code properly. can you help me ? it will be dummy proof.
Awesome.It worked. How should i narrow down the result? when i integer number 10 . It tell me it can be string , fixnum, float. How to get it is a fixnum ?
@lenso_here you can't :) It could be the string "10" or the number 10. The user just hit 1, 2 and enter, it's up to you to interpret the input.
it is getting complicated. 10 is an integer. and i want to get it recognized as integer. I think you helped a lot. Thank you so much. However, I'm still at chapter I of coding. So i need more time.
1

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.

Comments

0

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?

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.