Pregunta

Tengo una cadena, dicen '123', y quiero convertirlo a 123.

Sé que usted puede simplemente hacer some_string.to_i, pero que se convierte 'lolipops' a 0, que no es el efecto que tengo en mente.Yo quiero volar en mi cara cuando trato de convertir algo inválido, con un bonito y doloroso Exception.De lo contrario, no puedo distinguir entre un válido 0 y algo que simplemente no es un número.

EDITAR: Yo estaba buscando la forma estándar de hacerlo, sin regex engaño.

¿Fue útil?

Solución

Ruby tiene esta funcionalidad:

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

Como se señaló en la respuesta por José Pecoraro, quizás quieran ver de cadenas que sean válidas no números decimales, tales como aquellos que se están iniciando con 0x hex y 0b para los binarios, y potencialmente más difíciles de números empezando por el cero que se analiza como octal.

Ruby 1.9.2 añadido opcional de un segundo argumento para radix así, por encima de problema puede ser evitado:

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

Otros consejos

Esto podría funcionar:

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

También ser conscientes de los efectos que la actual aceptado solución puede tener en el análisis de hexadecimal, octal y binario los números:

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

En Ruby números que comienzan con 0x o 0X en hexadecimal, 0b o 0B son binarios, y sólo 0 son octal.Si este no es el comportamiento deseado puede que desee que se combinan con algunas de las otras soluciones que comprobar si la cadena coincide con un patrón de primera.Como el /\d+/ las expresiones regulares, etc.

Otro comportamiento inesperado con la aceptación de la solución (con 1.8, 1.9 es aceptar):

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

así que si usted no está seguro de lo que está pasando en, asegúrese de agregar un .to_s.

Me gusta Myron la respuesta, sino que sufre de la enfermedad de Ruby "Yo ya no uso Java/C# así que nunca lo voy a utilizar la herencia de nuevo".La apertura de cualquier clase puede estar lleno de peligros y deben utilizarse con moderación, especialmente cuando es parte de Ruby básico de la biblioteca.No estoy diciendo que no lo use nunca, pero por lo general es fácil de evitar, y que hay mejores opciones disponibles, por ejemplo,

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

Entonces, cuando usted desea utilizar una cadena que podría ser un número es claro lo que estamos haciendo y no darle una paliza a cualquier núcleo de la clase, por ejemplo,

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.

Usted puede agregar todo tipo de otros controles en la inicialización, como la comprobación de los números binarios, etc.Lo principal, es que Ruby es para la gente y para la gente significa la claridad.Nomenclatura de un objeto a través del nombre de la variable y su nombre de la clase que hace las cosas mucho más clara.

He tenido que lidiar con esto en mi último proyecto, y mi aplicación fue similar, pero un poco 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

Probablemente no sea la forma más limpia de hacerlo, pero debería funcionar.

Re: La respuesta de Chris

Su aplicación vamos a cosas como "1a" o "b2" a través de.Cómo acerca de este lugar:

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

Esta salida:

100
1a is invalid
b2 is invalid
t is invalid
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top