Safe integer parsing in Ruby

后端 未结 8 643
眼角桃花
眼角桃花 2020-12-02 06:51

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

I know you can simply do some_string.to_i, but that

相关标签:
8条回答
  • 2020-12-02 07:00

    This might work:

    i.to_i if i.match(/^\d+$/)
    
    0 讨论(0)
  • 2020-12-02 07:06

    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.

    0 讨论(0)
  • 2020-12-02 07:07

    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
    
    0 讨论(0)
  • 2020-12-02 07:10

    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
    
    0 讨论(0)
  • 2020-12-02 07:12

    Ruby has this functionality built in:

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

    As noted in answer by Joseph Pecoraro, you might want to watch for strings that are valid non-decimal numbers, such as those starting with 0x for hex and 0b for binary, and potentially more tricky numbers starting with zero that will be parsed as octal.

    Ruby 1.9.2 added optional second argument for radix so above issue can be avoided:

    Integer('23')                                     # => 23
    Integer('0x23')                                   # => 35
    Integer('023')                                    # => 19
    Integer('0x23', 10)
    # => #<ArgumentError: invalid value for Integer: "0x23">
    Integer('023', 10)                                # => 23
    
    0 讨论(0)
  • 2020-12-02 07:12

    Another unexpected behavior with the accepted solution (with 1.8, 1.9 is ok):

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

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

    0 讨论(0)
提交回复
热议问题