163 votes

Analyse sûre des nombres entiers en Ruby

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

Je sais que vous pouvez simplement faire some_string.to_i mais qui convertit 'lolipops' a 0 ce qui n'est pas l'effet que j'ai en tête. Je veux que ça m'explose à la figure quand j'essaie de convertir quelque chose d'invalide, avec une belle et douloureuse Exception . Sinon, je ne peux pas faire la différence entre un valide 0 et quelque chose qui n'est pas un numéro du tout.

EDITAR: Je cherchais la manière standard de le faire, sans avoir recours à l'utilisation de regex.

239voto

Slartibartfast Points 5469

Ruby intègre cette fonctionnalité :

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

Comme indiqué dans la réponse de Joseph Pecoraro vous voudrez peut-être surveiller les chaînes de caractères qui sont des nombres non décimaux valides, comme ceux qui débutent par 0x pour l'hexagone et 0b pour le binaire, et potentiellement des nombres plus compliqués commençant par zéro qui seront analysés en octal.

Ruby 1.9.2 a ajouté un second argument optionnel pour radix, ce qui permet d'éviter le problème ci-dessus :

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

31voto

Purfideas Points 1852

Ça pourrait marcher :

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

8 votes

PSA : en Ruby, ^ y $ ont des significations subtilement différentes comme métachars que dans la plupart des autres saveurs de regexp. Vous voulez probablement utiliser \A y \Z à la place.

1 votes

Pour être pédant, la mention de différentes ancres regex selon @pje peut être incorrecte selon le comportement souhaité. Envisagez plutôt d'utiliser \z à la place de \Z comme la description de l'ancre Z majuscule est : "Correspond à la fin de la chaîne. Si la chaîne de caractères se termine par un saut de ligne, elle correspond juste avant le saut de ligne " -- ruby-doc.org/core-2.1.1/Regexp.html

25voto

Joseph Pecoraro Points 2200

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

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

En Ruby, les nombres qui commencent par 0x o 0X sont hexagonales, 0b o 0B sont binaires, et juste 0 sont octaux. Si ce n'est pas le comportement souhaité, vous pouvez combiner cette solution avec d'autres qui vérifient d'abord si la chaîne correspond à un modèle. Comme le /\d+/ expressions régulières, etc.

1 votes

C'est ce que j'attendais de la conversion.

6 votes

En Ruby 1.9, vous pouvez passer la base comme deuxième argument.

17voto

Jaime Cham Points 510

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 une balise .to_s .

7 votes

Test en Ruby 1.9. Integer(:foobar) => impossible de convertir un symbole en Integer (TypeError)

9voto

iain Points 6208

J'aime la réponse de Myron mais elle souffre de la maladie de Ruby qui consiste à "Je n'utilise plus Java/C# donc je ne vais plus jamais utiliser l'héritage" . L'ouverture d'une classe peut être dangereuse et doit être utilisée avec parcimonie, notamment lorsqu'il fait partie de la bibliothèque principale de Ruby. Je ne dis pas qu'il ne faut jamais l'utiliser, mais qu'il est généralement facile de l'éviter et qu'il existe de meilleures options disponibles, par exemple

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

Ainsi, lorsque vous souhaitez utiliser une chaîne de caractères qui pourrait être un nombre, vous savez clairement ce que vous faites et vous n'encombrez aucune classe de base, par exemple

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 dans l'initialisation, comme la vérification des nombres binaires, etc. Mais l'essentiel, c'est que Ruby est fait pour les gens et être fait pour les gens signifie clarté . Nommer un objet via son nom de variable y son nom de classe rend les choses mucho plus clair.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X