156 votes

Comment tester si une chaîne de caractères est fondamentalement un nombre entier entre guillemets en utilisant Ruby

J'ai besoin d'une fonction, is_an_integer

  • "12".is_an_integer? retourne vrai.
  • "blah".is_an_integer? retourne faux.

Comment puis-je faire cela en Ruby ? J'écrirais bien une expression rationnelle, mais je suppose qu'il existe une aide pour cela que je ne connais pas.

1 votes

2 votes

Soyez prudent en utilisant des solutions reposant sur des expressions régulières. Les tests de référence montrent qu'elles s'exécutent beaucoup plus lentement que le code régulier.

204voto

Sarah Mei Points 10673

Eh bien, voici le moyen le plus simple :

class String
  def is_integer?
    self.to_i.to_s == self
  end
end

>> "12".is_integer?
=> true
>> "blah".is_integer?
=> false

Je ne suis pas d'accord avec les solutions qui provoquent une exception pour convertir la chaîne de caractères - les exceptions ne sont pas un flux de contrôle, et vous pourriez aussi bien le faire de la bonne façon. Cela dit, ma solution ci-dessus ne traite pas les entiers qui ne sont pas en base 10. Voici donc la façon de le faire sans recourir aux exceptions :

  class String
    def integer? 
      [                          # In descending order of likeliness:
        /^[-+]?[1-9]([0-9]*)?$/, # decimal
        /^0[0-7]+$/,             # octal
        /^0x[0-9A-Fa-f]+$/,      # hexadecimal
        /^0b[01]+$/              # binary
      ].each do |match_pattern|
        return true if self =~ match_pattern
      end
      return false
    end
  end

2 votes

Ne pourriez-vous pas remplacer self.to_i.to_s == self avec Integer self rescue false ?

8 votes

Vous pourriez, mais ce serait mal vu. Vous n'utilisez pas les exceptions comme flux de contrôle, et le code de personne ne devrait jamais contenir "rescue false" (ou "rescue true"). Quelques simples gsub'ing permettraient à ma solution de fonctionner pour les cas limites non spécifiés par le PO.

1 votes

Pour mémoire, je pense que le "rescue nil" en ligne est plutôt utile, du moins s'il est utilisé avec discrétion.

146voto

Rado Points 1999

Vous pouvez utiliser des expressions régulières. Voici la fonction avec les suggestions de @janm.

class String
    def is_i?
       !!(self =~ /\A[-+]?[0-9]+\z/)
    end
end

Une version éditée selon le commentaire de @wich :

class String
    def is_i?
       /\A[-+]?\d+\z/ === self
    end
end

Dans le cas où vous n'avez besoin de vérifier que les nombres positifs

  if !/\A\d+\z/.match(string_to_check)
      #Is not a positive number
  else
      #Is all good ..continue
  end

4 votes

Pas mal. En Ruby, on omet généralement le mot-clé "return" si la valeur de retour est générée dans la dernière expression de la fonction. Ceci renverra également une valeur entière de zéro, vous voulez probablement un booléen, donc quelque chose comme ! !(str =~ /^[-+] ?[0-9]+$/) le ferait. Ensuite, vous pourriez l'ajouter à String et laisser de côté l'argument, en utilisant "self" au lieu de "str", et ensuite vous pourriez changer le nom en "is_i ?" ...

2 votes

Merci. Je n'ai absolument aucune idée des conventions et pratiques de ruby. J'ai juste fait une recherche rapide sur ruby et les expressions régulières pour voir la syntaxe, j'ai modifié l'expression régulière pour l'appliquer au problème en question et je l'ai testée. C'est assez chouette en fait Je devrais peut-être y réfléchir plus longuement quand j'aurai plus de temps libre.

0 votes

Vous avez la bonne idée, mais cela ne correspond pas aux littéraux binaires ou hexagonaux (voir ma solution modifiée ci-dessous).

73voto

sepp2k Points 157757

Vous pouvez utiliser Integer(str) et voir si ça soulève :

def is_num?(str)
  !!Integer(str)
rescue ArgumentError, TypeError
  false
end

Il convient de souligner que, bien que cela revienne vrai pour "01" il ne l'est pas pour "09" simplement parce que 09 ne serait pas un entier littéral valide. Si ce n'est pas le comportement que vous souhaitez, vous pouvez ajouter 10 comme second argument de Integer Le nombre est donc toujours interprété en base 10.

50 votes

Mec... provoquer une exception juste pour convertir un nombre ? Les exceptions ne sont pas des flux de contrôle.

32 votes

Ce n'est pas le cas, mais c'est malheureusement la façon canonique de déterminer l'"intégrité" d'une chaîne en Ruby. Les méthodes utilisant #to_i sont juste trop brisés à cause de leur permissivité.

18 votes

Pour ceux qui se demandent pourquoi, Integer("09") n'est pas valide car le "0" le rend octal, et 9 n'est pas un nombre octal valide. osdir.com/ml/lang.ruby.general/2002-08/msg00247.html

26voto

Robert Klemme Points 1244

Tu peux faire une ligne directe :

str = ...
int = Integer(str) rescue nil

if int
  int.times {|i| p i}
end

ou même

int = Integer(str) rescue false

Selon ce que vous essayez de faire, vous pouvez aussi utiliser directement un bloc début-fin avec une clause de sauvetage :

begin
  str = ...
  i = Integer(str)

  i.times do |j|
    puts j
  end
rescue ArgumentError
  puts "Not an int, doing something else"
end

1 votes

En ce qui concerne le sujet "exception comme flux de contrôle" : puisque nous ne savons pas comment la méthode en question doit être utilisée, nous ne pouvons pas vraiment juger si les exceptions conviennent ou non. Si la chaîne de caractères est saisie et qu'elle doit être un nombre entier, le fait de fournir un nombre non entier justifierait une exception. Cependant, il se peut que la manipulation ne se fasse pas dans la même méthode et que nous nous contentions de faire Integer(str).times {|i| met i} ou autre.

8voto

August Lilleaas Points 25812
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. Il n'est pas préfixé par is_ . Je trouve ça idiot sur les méthodes de point d'interrogation, j'aime bien "04".integer? beaucoup mieux que "foo".is_integer? .
  2. Il utilise la solution raisonnable de sepp2k, qui passe pour "01" et autres.
  3. Orienté objet, yay.

1 votes

+1 pour l'avoir nommé #integer ?, -1 pour avoir encombré String avec lui :-P

1 votes

Où d'autre pourrait-il aller ? integer?("a string") ftl.

2 votes

String#integer? est le genre de patch commun que chaque codeur Ruby et son cousin aiment ajouter au langage, conduisant à des bases de code avec trois implémentations différentes subtilement incompatibles et à des ruptures inattendues. J'ai appris cela à mes dépens sur de gros projets Ruby.

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