Безопасный разбор целых чисел в Ruby
у меня есть строка, скажем '123' и я хочу, чтобы преобразовать его в 123.
Я знаю, что вы можете просто сделать some_string.to_i, но это преобразует 'lolipops' до 0, что не тот эффект, я имею в виду. Я хочу, чтобы он взорвался в моем лице, когда я пытаюсь преобразовать что-то недействительное, с приятным и болезненным Exception. В противном случае, я не могу отличить действительный 0 и то, что просто не число вообще.
EDIT: Я искал стандартный способ делать это без обмана регулярных выражений.
8 ответов:
Ruby имеет эту встроенную функциональность:
Integer('1001') # => 1001 Integer('1001 nights') # ArgumentError: invalid value for Integer: "1001 nights"как отмечено в ответе Джозеф Пекораро, вы можете следить за строками, которые являются допустимыми не десятичными числами, например, начиная с
0xдля hex и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
также имейте в виду, что текущее принятое решение может иметь влияние на разбор шестнадцатеричных, восьмеричных и двоичных чисел:
>> Integer('0x15') # => 21 >> Integer('0b10') # => 2 >> Integer('077') # => 63в рубиновых числах, которые начинаются с
0xили0Xнаговора,0bили0Bявляются двоичными, и просто0это восьмеричное. Если это не желаемое поведение, вы можете объединить его с некоторыми другими решениями, которые сначала проверяют, соответствует ли строка шаблону. Как и/\d+/регулярные выражения и т. д.
другое неожиданное поведение с принятым решением (с 1.8, 1.9 ОК):
>> Integer(:foobar) => 26017 >> Integer(:yikes) => 26025Так что если вы не уверены, что передается, убедитесь, что вы добавляете
.to_s.
мне нравится ответ Майрона, но он страдает от рубиновой болезни "Я больше не использую Java / C#, поэтому я никогда не буду использовать наследование снова". Открытие любого класса может быть чревато опасностью и должно использоваться экономно, особенно когда это часть основной библиотеки Руби. Я не говорю, что никогда не используйте его, но обычно его легко избежать и что есть лучшие варианты, например
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.можно добавить всевозможные другие проверки в инициализации, как проверка двоичных чисел и т. д. Но главное, что Рубин - это для людей, а быть для людей значит ясность. Именование объекта через его переменную name и имя класса делает вещи много понятнее.
мне пришлось иметь дело с этим в моем последнем проекте, и моя реализация была похожа, но немного другая:
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
someString = "asdfasd123" number = someString.to_i if someString != number.to_s puts "oops, this isn't a number" endвероятно, не самый чистый способ сделать это, но должен работать.
Re:Крис
ваша реализация давайте вещи, как "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
Comments