Seguro entero de análisis en Rubí
-
09-06-2019 - |
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.
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.
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