tags:

views:

2682

answers:

7

I have a string, say '123', and I want to convert it to 123.

I know you can simply do some_string.to_i, but that converts 'lolipops' to 0, which is not the effect I have in mind. I want it to blow up in my face when I try to convert something invalid, with a nice and painful Exception. Otherwise, I can't distinguish between a valid 0 and something that just isn't a number at all.

EDIT: I was looking for the standard way of doing it, without regex trickery. Thanks all.

+44  A: 

there is builtin stuff for it

Integer('1001')                                    # => 1001  
Integer('1001 nights')  
# ArgumentError: invalid value for Integer: "1001 nights"

nuff said.

Slartibartfast
"1001 nights".to_i would work. ;)
unexist
+2  A: 

This might work:

i.to_i if i.match(/^\d+$/)
Purfideas
A: 
   someString = "asdfasd123"
   number = someString.to_i
   if someString != number.to_s
          puts "oops, this isn't a number"
   end

probably not the cleanest way to do it, but should work

Paul Wicks
+8  A: 

Also be aware of the affects that the current accepted solution may have on parsing hex, octal, and binary numbers:

>> Integer('0x15')
# => 21  
>> Integer('0b10')
# => 2  
>> Integer('077')
# => 63

In Ruby numbers that start with 0x or 0X are hex, 0b or 0B are binary, and just 0 are octal. If this is not the desired behavior you may want to combine that with some of the other solutions that check if the string matches a pattern first. Like the /\d+/ regular expressions, etc.

Joseph Pecoraro
That's what I'd expect from the conversion though
wvdschel
A: 

Re: Chris's answer

Your implementation let's things like "1a" or "b2" through. How about this instead:

def safeParse2(strToParse)
  if strToParse =~ /\A\d+\Z/
    strToParse.to_i
  else
    raise Exception
  end
end

["100", "1a", "b2", "t"].each do |number|
  begin
    puts safeParse2(number)
  rescue Exception
    puts "#{number} is invalid"
  end
end

This outputs:

100
1a is invalid
b2 is invalid
t is invalid
metavida
Ah, good catch! Totally missed that easy mistake.
Chris Bunch
+2  A: 

I had to deal with this in my last project, and my implementation was similar, but a bit different:

class NotAnIntError < StandardError 
end

class String
  def is_int?    
    self =~ /^-?[0-9]+$/
  end

  def safe_to_i
    return self.to_i if is_int?
    raise NotAnIntError, "The string '#{self}' is not a valid integer.", caller
  end
end

class Integer
  def safe_to_i
    return self
  end            
end

class StringExtensions < Test::Unit::TestCase

  def test_is_int
    assert "98234".is_int?
    assert "-2342".is_int?
    assert "02342".is_int?
    assert !"+342".is_int?
    assert !"3-42".is_int?
    assert !"342.234".is_int?
    assert !"a342".is_int?
    assert !"342a".is_int?
  end

  def test_safe_to_i
    assert 234234 == 234234.safe_to_i
    assert 237 == "237".safe_to_i
    begin
      "a word".safe_to_i
      fail 'safe_to_i did not raise the expected error.'
    rescue NotAnIntError 
      # this is what we expect..
    end
  end

end
+2  A: 

Another unexpected behavior with the accepted solution:

>> Integer(:foobar)
=> 26017
>> Integer(:yikes)
=> 26025

so if you're not sure what is being passed in, make sure you add a ".to_s".

Jaime Cham