Безопасный целочисленный парсинг в Ruby

155
задан the Tin Man 20 September 2012 в 23:35
поделиться

7 ответов

Ruby встроили эту функциональность:

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

, Как отмечено в ответе Joseph Pecoraro , Вы могли бы хотеть наблюдать за строками, которые являются допустимыми недесятичными числами, такими как те, которые запускаются с 0x для шестнадцатеричного числа и 0b для двоичного файла и потенциально более хитрых чисел, запускающихся с нуля, который будет проанализирован как восьмеричный.

Ruby 1.9.2 добавил дополнительный второй аргумент в пользу основания, таким образом, выше проблемы может избежаться:

Integer('23')                                     # => 23
Integer('0x23')                                   # => 35
Integer('023')                                    # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10)                                # => 23
229
ответ дан Community 23 November 2019 в 21:55
поделиться

Это могло бы работать:

i.to_i if i.match(/^\d+$/)
27
ответ дан Purfideas 23 November 2019 в 21:55
поделиться

Также знайте о влиянии, которое текущее принятое решение может иметь при парсинге шестнадцатеричного числа, восьмеричных, и двоичных чисел:

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

В числах Ruby, которые запускаются с 0x или 0X, шестнадцатеричное число, 0b или 0B являются двоичными, и всего 0 являются восьмеричными. Если это не желаемое поведение, можно хотеть объединить это с некоторыми из других решений, которые проверяют, соответствует ли строка шаблону сначала. Как эти /\d+/ регулярные выражения, и т.д.

24
ответ дан Joseph Pecoraro 23 November 2019 в 21:55
поделиться

Я должен был иметь дело с этим в своем последнем проекте, и моя реализация была подобна, но немного отличалась:

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
6
ответ дан 23 November 2019 в 21:55
поделиться
someString = "asdfasd123"
number = someString.to_i
if someString != number.to_s
  puts "oops, this isn't a number"
end

, Вероятно, не самый чистый способ сделать это, но должен работать.

2
ответ дан dusan 23 November 2019 в 21:55
поделиться

Ре: ответ Chris

Ваша реализация позволяют нам вещи как "1a" или "b2" через. Как насчет этого вместо этого:

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

Это производит:

100
1a is invalid
b2 is invalid
t is invalid
1
ответ дан Community 23 November 2019 в 21:55
поделиться

Еще одно неожиданное поведение с принятым решением (с 1.8, 1.9 в порядке):

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

поэтому, если вы не уверены, что передается, убедитесь, что вы добавили .to_s .

15
ответ дан 23 November 2019 в 21:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: