string - Ruby中的安全整数解析

我有一个字符串,比如some_string.to_i,我想把它转换为'lolipops'

我知道你可以简单地做some_string.to_i,但是将'lolipops'转换为0,这不是我想到的效果。 当我试图转换一些无效的东西时,我希望它能在我的脸上爆炸,带着一个美好而痛苦的Exception。否则,我无法区分有效的0和根本不是数字的东西。

编辑:我一直在寻找标准的做法,没有正则表达式的诡计。

wvdschel asked 2019-05-05T11:27:33Z
8个解决方案
216 votes

Ruby内置了这个功能:

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

正如Joseph Pecoraro在回答中所指出的,您可能想要查看有效非十进制数的字符串,例如以二进制的0x和二进制的0b开头的字符串,以及可能被解析为八进制的从零开始的更棘手的数字。

Ruby 1.9.2为radix添加了可选的第二个参数,因此可以避免上述问题:

Integer('23')                                     # => 23
Integer('0x23')                                   # => 35
Integer('023')                                    # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10)                                # => 23
Slartibartfast answered 2019-05-05T11:28:06Z
25 votes

这可能有效:

i.to_i if i.match(/^\d+$/)
Purfideas answered 2019-05-05T11:28:28Z
25 votes

还要注意当前接受的解决方案可能对解析十六进制,八进制和二进制数的影响:

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

在以0x0X开头的Ruby数字是十六进制,0b0B是二进制,只有0是八进制。 如果这不是所需的行为,您可能希望将其与一些其他解决方案相结合,以检查字符串是否与模式匹配。 像/\d+/正则表达式等

Joseph Pecoraro answered 2019-05-05T11:29:06Z
14 votes

接受的解决方案的另一个意外行为(1.8,1.9是可以的):

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

因此,如果您不确定传入的是什么,请确保添加.to_s

Jaime Cham answered 2019-05-05T11:29:41Z
9 votes

我喜欢Myron的答案,但它患有Ruby疾病“我不再使用Java / C#所以我永远不会再使用继承”了。 打开任何课程都会充满危险,应该谨慎使用,特别是当它是Ruby核心库的一部分时。 我不是说不要使用它,但它通常很容易避免,并且有更好的选择,例如

class IntegerInString < String

  def initialize( s )
    fail ArgumentError, "The string '#{s}' is not an integer in a string, it's just a string." unless s =~ /^\-?[0-9]+$/
    super
  end
end

然后,当你想使用一个可能是数字的字符串时,你可以清楚地知道你正在做什么,而且你不会破坏任何核心类,例如

n = IntegerInString.new "2"
n.to_i
# => 2

IntegerInString.new "blob"
ArgumentError: The string 'blob' is not an integer in a string, it's just a string.

您可以在初始化中添加各种其他检查,例如检查二进制数等。但最重要的是,Ruby适用于人,而适用于人则意味着清晰。 通过变量名称及其类名命名对象使事情更加清晰。

iain answered 2019-05-05T11:30:25Z
6 votes

我在上一个项目中不得不处理这个问题,我的实现类似,但有点不同:

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
answered 2019-05-05T11:30:53Z
2 votes
someString = "asdfasd123"
number = someString.to_i
if someString != number.to_s
  puts "oops, this isn't a number"
end

可能不是最干净的方式,但应该工作。

Paul Wicks answered 2019-05-05T11:31:24Z
1 votes

回复:克里斯的回答

你的实现让“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
metavida answered 2019-05-05T11:32:01Z
translate from https://stackoverflow.com:/questions/49274/safe-integer-parsing-in-ruby