2008-09-08 9 views
139

Tengo una cadena, digamos '123', y quiero convertirla a 123.Análisis entero seguro en Ruby

Sé que puede simplemente hacer some_string.to_i, pero eso convierte 'lolipops' en , que no es el efecto que tengo en mente. Quiero que explote en mi cara cuando trato de convertir algo inválido, con un agradable y doloroso Exception. De lo contrario, no puedo distinguir entre un 0 válido y algo que simplemente no es un número en absoluto.

EDIT: Estaba buscando la forma estándar de hacerlo, sin trucos regex.

+0

safety third !!! – jsh

Respuesta

207

Ruby tiene esta funcionalidad incorporada en:

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

Como se señaló en la respuesta por Joseph Pecoraro, es posible que desee ver para las cadenas que son números no decimales válidos, tales como los que se inician con 0x para hexagonal y 0b para números binarios y potencialmente más complicados comenzando con cero que se analizará como octal.

de Ruby 1.9.2 añadió segundo argumento opcional para radix menos por encima 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 
24

Esto podría funcionar:

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

+1 por ser corto y dulce. –

+8

PSA: en Ruby, '^' y '$' [tienen significados sutilmente diferentes] (http://stackoverflow.com/a/5979643/1004889) como metacars que en la mayoría de los otros sabores de expresiones regulares. Probablemente quieras utilizar '\ A' y' \ Z' en su lugar. – pje

+2

Esto no funcionará para que los números negativos –

1
someString = "asdfasd123" 
number = someString.to_i 
if someString != number.to_s 
    puts "oops, this isn't a number" 
end 
Probablemente no

la manera más limpia de hacerlo , pero debería funcionar

25

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

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

En los números de Ruby que comienzan con 0x o 0X son hexagonal, 0b o 0B son binario, y solo 0 son octal. Si este no es el comportamiento deseado, puede combinarlo con algunas de las otras soluciones que comprueban si la cadena coincide primero con un patrón. Al igual que los /\d+/ expresiones regulares, etc.

+1

Eso es lo que esperaría de la conversión a través de – wvdschel

+5

En Ruby 1.9, puede pasar la base como segundo argumento. –

1

Re: Chris's answer

de su aplicación vamos a cosas como "1a" o "b2" a través. ¿Qué hay de esto en su 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 

Este salidas:

100 
1a is invalid 
b2 is invalid 
t is invalid 
+0

¡Ah, buen truco! Perdí por completo ese fácil error. –

+0

para ser pedante, la mención de diferentes anclajes regex según @pje y utilizada puede ser incorrecta dependiendo del comportamiento deseado. En su lugar, considere usar '\ z' en lugar de' \ Z' ya que la descripción del anclaje Z en mayúscula es: "Coincide con el final de la cadena. Si la cadena termina con una nueva línea, coincide justo antes de la nueva línea" - http: // ruby -doc.org/core-2.1.1/Regexp.html – Del

6

que tenía que hacer frente a 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 
14

Otro comportamiento inesperado con la solución aceptada (con 1.8, 1.9 está bien):

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

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

+5

prueba en Ruby 1.9. Integer (: foobar) => no puede convertir Symbol en Integer (TypeError) – GutenYe

7

Me gusta la respuesta de Myron pero sufre de la enfermedad de Ruby de "Ya no uso Java/C#, así que nunca volveré a utilizar la herencia". Abrir cualquier clase puede ser peligroso y se debe usar con moderación, especialmente cuando es parte de la biblioteca principal de Ruby. No estoy diciendo que nunca lo use, pero por lo general es fácil de evitar y que hay mejores opciones disponibles, p.

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 

Luego, cuando se desea utilizar una cadena que podría ser un número que está claro lo que está haciendo y no Clobber cualquier clase principal, 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 el inicialización, como la comprobación de números binarios, etc. Lo más importante sin embargo, es que Ruby es para las personas y el bienestar de las personas significa claridad. Nombrar un objeto a través de su nombre de variable y su nombre de clase hace que las cosas sean mucho más claras.

Cuestiones relacionadas