Question

J'ai une chaîne, dis '123', et je veux le convertir en 123.

Je sais que tu peux simplement le faire some_string.to_i, mais cela convertit 'lolipops' à 0, ce qui n'est pas l'effet que j'ai en tête.Je veux que ça m'explose au visage quand j'essaie de convertir quelque chose d'invalide, avec un bruit agréable et douloureux Exception.Sinon, je ne peux pas faire la distinction entre un valide 0 et quelque chose qui n'est tout simplement pas un nombre.

MODIFIER: Je cherchais la manière standard de le faire, sans supercherie des regex.

Était-ce utile?

La solution

Ruby a cette fonctionnalité intégrée :

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

Comme indiqué dans la réponse de Joseph Pecoraro, vous souhaiterez peut-être surveiller les chaînes qui sont des nombres non décimaux valides, tels que ceux commençant par 0x pour l'hexagone et 0b pour les nombres binaires et potentiellement plus délicats commençant par zéro qui seront analysés comme octaux.

Ruby 1.9.2 a ajouté un deuxième argument facultatif pour radix afin que le problème ci-dessus puisse être évité :

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

Autres conseils

Cela pourrait fonctionner :

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

Soyez également conscient des effets que la solution actuellement acceptée peut avoir sur l'analyse des nombres hexadécimaux, octaux et binaires :

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

En Ruby, les nombres commençant par 0x ou 0X sont hexagonaux, 0b ou 0B sont binaires, et juste 0 sont octaux.Si ce n'est pas le comportement souhaité, vous souhaiterez peut-être le combiner avec certaines des autres solutions qui vérifient d'abord si la chaîne correspond à un modèle.Comme le /\d+/ expressions régulières, etc.

Un autre comportement inattendu avec la solution acceptée (avec 1.8, 1.9 est ok) :

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

donc si vous n'êtes pas sûr de ce qui est transmis, assurez-vous d'ajouter un .to_s.

J'aime la réponse de Myron mais il souffre de la maladie Ruby de "Je n'utilise plus Java/C# donc je n'utiliserai plus jamais l'héritage".L'ouverture de n'importe quel cours peut être pleine de dangers et doit être utilisée avec parcimonie, en particulier quand il fait partie de la bibliothèque principale de Ruby.Je ne dis pas de ne jamais l'utiliser, mais il est généralement facile de l'éviter et il existe de meilleures options disponibles, par ex.

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

Ensuite, lorsque vous souhaitez utiliser une chaîne qui pourrait être un nombre, ce que vous faites est clair et vous n'encombrez aucune classe principale, par ex.

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.

Vous pouvez ajouter toutes sortes d'autres vérifications lors de l'initialisation, comme la vérification des nombres binaires, etc.L'essentiel cependant, c'est que Ruby est pour les gens et qu'être pour les gens signifie clarté.Nommer un objet via son nom de variable et son nom de classe fait des choses beaucoup plus clair.

J'ai dû gérer cela dans mon dernier projet, et ma mise en œuvre était similaire, mais un peu différente :

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

Ce n'est probablement pas la façon la plus propre de le faire, mais cela devrait fonctionner.

Concernant: La réponse de Chris

Votre implémentation laisse passer des éléments comme "1a" ou "b2".Que diriez-vous plutôt de ceci :

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

Cela produit :

100
1a is invalid
b2 is invalid
t is invalid
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top