Pergunta

Eu tenho uma corda, digamos '123', e quero convertê-lo para 123.

Eu sei que você pode simplesmente fazer some_string.to_i, mas isso converte 'lolipops' para 0, que não é o efeito que tenho em mente.Quero que isso exploda na minha cara quando tento converter algo inválido, com um sentimento agradável e doloroso. Exception.Caso contrário, não consigo distinguir entre um válido 0 e algo que simplesmente não é um número.

EDITAR: Eu estava procurando a maneira padrão de fazer isso, sem truques de regex.

Foi útil?

Solução

Ruby tem esta funcionalidade incorporada:

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

Conforme observado na resposta de José Pecoraro, talvez você queira observar strings que sejam números não decimais válidos, como aqueles que começam com 0x para hexadecimal e 0b para números binários e potencialmente mais complicados começando com zero que serão analisados ​​​​como octais.

Ruby 1.9.2 adicionou um segundo argumento opcional para radix para que o problema acima possa ser evitado:

Integer('23')                                     # => 23
Integer('0x23')                                   # => 35
Integer('023')                                    # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10)                                # => 23

Outras dicas

Isso pode funcionar:

i.to_i if i.match(/^\d+$/)

Esteja ciente também dos efeitos que a solução atualmente aceita pode ter na análise de números hexadecimais, octais e binários:

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

Em números Ruby que começam com 0x ou 0X são hexadecimais, 0b ou 0B são binários e apenas 0 são octais.Se este não for o comportamento desejado, você pode querer combiná-lo com algumas das outras soluções que verificam primeiro se a string corresponde a um padrão.Como o /\d+/ expressões regulares, etc.

Outro comportamento inesperado com a solução aceita (com 1.8, 1.9 está ok):

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

então, se você não tiver certeza do que está sendo transmitido, adicione um .to_s.

Gosto da resposta de Myron, mas ela sofre da doença Ruby de "Eu não uso mais Java/C#, então nunca mais usarei herança".Abrir qualquer aula pode ser muito perigoso e deve ser usado com moderação, especialmente quando faz parte da biblioteca principal do Ruby.Não estou dizendo para nunca usá-lo, mas geralmente é fácil de evitar e existem opções melhores disponíveis, por ex.

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

Então, quando você deseja usar uma string que pode ser um número, fica claro o que você está fazendo e você não destrói nenhuma classe principal, por exemplo.

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.

Você pode adicionar todos os tipos de outras verificações na inicialização, como verificação de números binários, etc.O principal, porém, é que Ruby é para pessoas e ser para pessoas significa clareza.Nomeando um objeto através do nome de sua variável e seu nome de classe faz as coisas muito mais claro.

Tive que lidar com isso em meu último projeto, e minha implementação foi semelhante, mas um pouco diferente:

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

Provavelmente não é a maneira mais limpa de fazer isso, mas deve funcionar.

Ré: A resposta de Chris

Sua implementação permite coisas como "1a" ou "b2".Que tal isso:

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

Isso produz:

100
1a is invalid
b2 is invalid
t is invalid
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top