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.

È stato utile?

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top