Ruby中的安全整数parsing
我有一个string,说'123'
,我想将其转换为123
。
我知道你可以简单地做some_string.to_i
,但是将'lolipops'
转换为0
,这不是我想到的效果。 当我尝试将一些无效的东西转换成非常痛苦的Exception
时,我想让它在我的脸上炸开。 否则,我无法区分一个有效的0
和一个根本不是数字的东西。
编辑:我正在寻找这样做的标准方式,没有正则expression式欺骗。
Ruby有这样的function:
Integer('1001') # => 1001 Integer('1001 nights') # ArgumentError: invalid value for Integer: "1001 nights"
正如Joseph Pecoraro在回答中指出的那样,您可能需要监视非有效十进制数字的string,例如以hex0x
和二进制0x
的0b
,以及可能更复杂的以零开头的数字,将被parsing为八进制。
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
还要注意当前接受的解决scheme可能对parsinghex,八进制和二进制数字的影响:
>> Integer('0x15') # => 21 >> Integer('0b10') # => 2 >> Integer('077') # => 63
在Ruby中,以0x
或0X
0x
数字是hex, 0b
或0B
是二进制的,而0
是八进制的。 如果这不是所需的行为,您可能希望将其与其他解决scheme结合使用,以检查string是否与模式匹配。 像/\d+/
正则expression式一样,等等
这可能工作:
i.to_i if i.match(/^\d+$/)
接受的解决scheme的另一个意想不到的行为(与1.8,1.9是好的):
>> Integer(:foobar) => 26017 >> Integer(:yikes) => 26025
所以如果您不确定传入的内容,请确保添加.to_s
。
我喜欢Myron的答案,但是患有“我不再使用Java / C#的ruby病”,所以我再也不会使用inheritance了 。 打开任何类都可能充满危险,应该谨慎使用, 特别是当它是Ruby核心库的一部分时。 我并不是说永远不要使用它,但通常很容易避免,而且有更好的select,例如
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
那么当你希望使用一个可以是数字的string时,你很清楚你在做什么,而不会打破任何核心类,例如
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是为人们而为人们的意思是清晰的 。 通过variables名和类名命名对象使得事情变得更加清晰。
在我上一个项目中,我不得不处理这个问题,而且我的实现是相似的,但有点不同:
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
回复: 克里斯的回答
你的实现让事情像“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
someString = "asdfasd123" number = someString.to_i if someString != number.to_s puts "oops, this isn't a number" end
可能不是最干净的方法,但应该工作。