Cassetta di sicurezza integer analisi in Ruby
-
09-06-2019 - |
Domanda
Ho una stringa di dire '123'
, e voglio convertirlo 123
.
So che si può semplicemente fare some_string.to_i
, ma che converte 'lolipops'
per 0
, che non è l'effetto che ho in mente.Voglio che soffiano in faccia quando provo a convertire qualcosa di valido, con una bella e dolorosa Exception
.Altrimenti, non riesco a distinguere tra una valida 0
e qualcosa che non è solo un numero.
EDIT: Stavo cercando il modo standard di fare, senza regex inganno.
Soluzione
Ruby ha questa funzionalità:
Integer('1001') # => 1001
Integer('1001 nights')
# ArgumentError: invalid value for Integer: "1001 nights"
Come indicato nella risposta Giuseppe Pecoraro, si potrebbe desiderare di guardare per le stringhe che sono validi, non di numeri decimali, come quelli di partenza con 0x
per hex e 0b
per binario, e potenzialmente più difficile a partire dal numero zero, che verrà analizzato come ottale.
Ruby 1.9.2 aggiunto il secondo argomento opzionale per radice così al di sopra di problema può essere evitato:
Integer('23') # => 23
Integer('0x23') # => 35
Integer('023') # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10) # => 23
Altri suggerimenti
Questo potrebbe funzionare:
i.to_i if i.match(/^\d+$/)
Anche essere consapevoli dell'influenza che l'attuale soluzione accettata può avere sull'analisi esadecimale, ottale e binario numeri:
>> Integer('0x15')
# => 21
>> Integer('0b10')
# => 2
>> Integer('077')
# => 63
In Ruby numeri che iniziano con 0x
o 0X
sono hex, 0b
o 0B
sono binari e basta 0
sono ottale.Se questo non è il comportamento desiderato è possibile che si desidera combinare con alcune delle altre soluzioni che controllare se la stringa corrisponde a un primo modello.Come il /\d+/
le espressioni regolari, etc.
Un altro imprevisto con la soluzione accettata (con la 1.8, 1.9 è ok):
>> Integer(:foobar)
=> 26017
>> Integer(:yikes)
=> 26025
quindi, se non siete sicuri di ciò che viene passato in, assicurarsi di aggiungere un .to_s
.
Mi piace Myron risposta ma soffre di Ruby malattia di "Non sono più io uso Java/C# in modo da sto andando mai utilizzare l'ereditarietà di nuovo".L'apertura di qualsiasi classe possono essere irto di pericoli e dovrebbe essere usato con parsimonia, in particolare quando è parte di Ruby core library.Non sto dicendo che non usa mai, ma è di solito facile da evitare e che ci sono più opzioni disponibili, ad esempio
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
Poi, quando si desidera utilizzare una stringa che può essere un numero, è chiaro di cosa si sta facendo e non sovrascrive qualsiasi classe di base, ad es.
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.
È possibile aggiungere tutti i tipi di altri controlli nella inizializzare, come il controllo per i numeri binari etc.La cosa principale, però, è che Ruby è per persone e per le persone significa chiarezza.Denominazione di un oggetto tramite il suo nome di variabile e il nome di classe rende le cose molto più chiara.
Ho avuto a che fare con questo, con il mio ultimo progetto, e la mia realizzazione è stata simile, ma un po ' diverso:
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
Probabilmente non è il modo più pulito per farlo, ma dovrebbe funzionare.
Re: Chris risposta
Implementazione diciamo cose come "1a" o "b2" attraverso.Come su questo invece:
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
Questo output:
100
1a is invalid
b2 is invalid
t is invalid